迷宫问题的探讨

迷宫问题的探讨

1.研究背景

前几天,有同学问我,这道题目怎么做啊。哪道题目呢?那你就继续看吧。

2.真题重现

(1)题目描述:

在一家博物馆里,有一件珍贵的珠宝放在一个房间里。博物馆的房间以NxM网格的形式呈现。博物馆的所有都与相邻的房间相邻。有一名叫山姆的小偷打算偷这件珠宝。他设法弄到了一些房间(包括放置宝石的房间)的钥匙。他从网格左上角的单元格所表示的房间进入博物馆。山姆有钥匙的房间表示为1,山姆没有钥匙的房间表示为0,有珠宝的房间表示为9.

写一个算法,如果山姆到达有珠宝的房间,输出1,否则输出0。

(2)输入和输出:

输入:

    该函数/方法 的输入包括三个参数--------

        1)rows,一个整数,表示网格grid中的行数(N);

        2)columns,一个整数,表示网格grid中的列数(M);

        3)grid,表示一个二维的整数网格。

输出:

如果山姆到达有珠宝的房间,则返回整数1,否则返回整数0。

(3)注意:

山姆不能对角移动。

(4)示例1:

输入:

3,3,{ {1,1,1},{9,1,1}, {0,1,0}}

期望的输出:1

(5)示例2:

输入:

3,3,{{0,0,0},{9,1,1},{0,1,1}}

期望的输出:0

3.数学表达

  在平面直角座标系的第一象限内,我们来探讨如下问题。对于给定的两个正整数M(M>100)、N(N>100),在第一象限内有任意一点(x,y),满足x,y为正整数,且x<M,y<N,有f(x,y)=0或1。有一只蚂蚁从原点(0,0)出发,该蚂蚁只能在第一象限内上、下、左、右爬行,且每次爬行的长度为一个单位,即蚂蚁所在的位置(a,b)一定满足a、b为自然数。若f(x,y) = 0,则蚂蚁不可以到达点(x,y),否则蚂蚁可以到达该点。若给定任一点(m,n),有0<m<M,0<n<N,求解该蚂蚁是否能够到达该点的算法。

4.算法设计与分析

(1)分析

    经过思考,发现从原点出发的蚂蚁(或山姆),要到达指定点(珠宝的位置),应该像走迷宫一样尝试所有可能,寻找出一条路径来为止。为说明方便,这里给每一个座标编号,只是为了说明方便,在实际的编码中,不会加入这里的编号,而是使用座标。如下图:

 

    我们从左至右,从上至下依次编号。上图中原点(0,0)的编号为1;点(1,0)编号为2;点(2,0)编号为3;点(0,1)编号为7。

因此对于0<x<M,0<y<N的范围,点(x,y)编号为M*y+x+1。

对于上图,可以用如下的矩阵描述:

 

    对于走迷宫,我们应该不断尝试新的可到达路径,对于每一个岔路都应该去尝试,直到到达指定出口,这里的珠宝位置就是出口;如果走进了死胡同,我们应该回溯,就是换一条路继续探索;另外还要防止死循环。综上,每到达一个岔路我们都应该标记一下,进入死循环或走进死胡同的时候回到岔路换一条路继续走。如下图:

    这里的位置1就是一个岔路,因为有两条路可以走,即1->7或1->2。因此1位置要做一个标记。另外,为避免是循环,如1->2->8->7->1,此时又回到了1位置,因此1位置要做一个标记。

    最后,我们每走一步,就在新位置上选择新的路径,那么我们的路径长度是一个增长的过程,并且增长的过程中还存在舍弃某些路径重新选取岔路的可能。所以,大体上,路径长度不断增加,新的点又可以不断增加进来或丢弃。因此,我们需要用“先进后出”的数据结构----栈!

(2)栈的push和pop操作

    首先从原点出发,也就是编号为1的点('编号为N的点'简称为'点N')出发,1入栈,此时栈中只有点1,那么栈如下图所示:

然后发现点1是一个岔路,因为点1可以到达点7,也可以到达点2,那么1->7,1->2依次入栈。栈如下图所示

 

此时,我们到达了点2,发现点2可以连通点8和点3,于是2->8,2->3入栈,栈如下图所示。

然后我们到达了点3,3只能和点9连通不能和点4连通,于是3->9入栈。假设此时到了3发现是一个死胡同,即3不能和9连通,也不能和4连通。那么此时3就应该出栈,出栈后我们应该在路径1->2->8的基础上寻找出路。下图是假设3为死胡同时,3出栈后的新栈,如下图:

        我们就这样反反复复地出栈入栈,直到我们到达指定的点,也就是目标点(此处为点31)出现在栈里面。或者最后发现栈为空,说明目标不可达。

5.编码

(1)设计迷宫栈

这里,我们使用C++语言来进行编码。我们需要一个栈,命名为LabyrinthStack。定义如下

stack<MyPoint *> LabyrinthStack;

可以用一个数组来保存路径内容,故栈中元素类型为一个个数组,数组中每个元素是一个点,故为MyPoint类型。

很容易知道数组最大长度为M*N,即迷宫中所有的点的个数。MyPoint类型定义为:

typedef struct myPoint
{
    int x;//横座标
    int y;//纵座标
    int iFork;//该点是否为岔口
    int nValue;//该点是否可以到达value=1表示可以,value=0表示不可以
    int nflag;//该点是否已经入栈,1入栈;0未入栈。
    int index;//索引,ID号,其中index = M*y+x+1,MxN表示迷宫矩阵大小
}MYPOINT,*pMYPOINT;

 

一个二维数组

(2)功能函数

我们需要一个函数,来找到指定点相互连通的函数,命名为IsAccess。

如下:

/*该函数判断该点是否可达

输入:x,点的横座标

       y,点的纵座标

输出:不可达-1

        可达:0

*/

int IsAccess(int x,int y)

{

    if(x>=0 && x < M && y>=0 && y<N && grid[x][y] = 1)

    {

        return 0;

    }

    else

    {

        return -1;

    }

}

(3)完整编码

    以下C++代码在linux操作系统上G++未编译,没测试通过,功能也没通过(哈哈哈哈...)。

    (未完,待续...)


 

#include <stack>
#include <iostream>

using namespace std;

typedef struct myPoint
{
	int x;//横座标
	int y;//纵座标
	int iFork;//该点是否为岔口
	int nValue;//该点是否可以到达value=1表示可以,value=0表示不可以
	int nflag;//该点是否已经入栈,1入栈;0未入栈。
	int index;//索引,ID号,其中index = M*y+x+1,MxN表示迷宫矩阵大小
}MYPOINT,*pMYPOINT;

int main(int argc, char *argv[])
{
        stack<MYPOINT> LabyrinthStack;
        int i = 0;



        for(i=0;i<10;i++)
        {
				MYPOINT a;
				a.x = i;
				a.y = i;
				a.iFork = 1;
				a.index = i;
                s.push(a);
        }
		cout<<"+=============================================+"<<endl;
        while(!s.empty())
        {
				MYPOINT b = s.top();
                s.pop();
        }
        return 0;
}

/*该函数判断该点是否可达

输入:x,点的横座标

       y,点的纵座标

输出:不可达-1

        可达:0

*/

int IsAccess(int x,int y, int M, int N)
{

    if(x>=0 && x < M && y>=0 && y<N && grid[x][y] = 1)

    {//既然可以到达,那就在这里入栈呗
		
        return 0;

    }

    else

    {

        return -1;

    }

}

bool isJewelry(int x,int y,int **grid)
{
	if(grid[x][y] == 9)
	{
		return true;
	}

	return false;
}

int isPath(int rows, int columns, int **grid)
{//MxN表示迷宫矩阵大小,即rows x columns表示矩阵的大小
  MYPOINT pointSet[rows*columns];//所有点。
  
  int i = 0;
  int j = 0;
  int M = ROWS;
  int N = columns;
  
  //初始化所有点
  for(i=0;i<M;++i)
  {
	  for(j=0;j<N;++j)
	  {
		  pointSet[M*j+i+1].x = i;//横座标
		  pointSet[M*j+i+1].y = j;
		  pointSet[M*j+i+1].iFork = 0;
		  pointSet[M*j+i+1].nflag = 0;//此处所有点都没有入栈哦
		  pointSet[M*j+i+1].nValue = grid[i][j];
	  }
  }
  
  LabyrinthStack.push(pointSet[0]);//从原点出发
  while(!LabyrinthStack.empty)
  {
	  MYPOINT topElement = LabyrinthStack.top()
	  int x = topElement.x;//栈顶元素横座标 M*y+x+1
	  int y = topElement.y;//栈顶元素纵座标
	  int fork = 0;
	  
	  if(!IsAccess(x+1,y))
	  {
		if(isJewelry(x+1,y,grid))
		{
			return 1;//已经到达了珠宝的房间
		}			
		LabyrinthStack.push(pointSet[M*y+x+1+1]);
	  }
	  
	  if(!IsAccess(x,y+1))
	  {
		if(isJewelry(x,y+1,grid))
		{
			return 1;//已经到达了珠宝的房间
		}	
		LabyrinthStack.push(pointSet[M*(y+1)+x+1]);
	  }
	  
	  if(!IsAccess(x-1,y))
	  {
		if(isJewelry(x-1,y,grid))
		{
			return 1;//已经到达了珠宝的房间
		}	
		LabyrinthStack.push(pointSet[M*y+x]);
	  }
	  
	  if(!IsAccess(x,y-1))
	  {
		if(isJewelry(x,y-1,grid))
		{
			return 1;//已经到达了珠宝的房间
		}	
		LabyrinthStack.push(pointSet[M*(y-1)+x+1]);
	  }
	  
	  if(fork == 0)
	  {//该点是死胡同,该点出栈
		  LabyrinthStack.pop(pointSet[M*y+x+1]);
	  }
  }


  return 0;//栈为空,山姆找不到通向珠宝房间的路线,返回0
}

 

 

 

 

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章