迷宮問題的探討

迷宮問題的探討

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
}

 

 

 

 

 

 

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