廣度優先遍歷隊列實現

本週做了兩道題,Fast Robot和Picking up Jewels

先說Fast Robot,要求找出從起點到終點最少拐彎次數。

這道題其實有一種很簡單的算法:

1.      從起點開始,將拐1次彎的點全部入隊,map[i][j].path全部標記成1(並且標記爲已訪問),然後起點出隊。

2.      拐一次彎的第一個點出隊,將第一個點作爲起點,所有拐一次彎並且未入隊的點入隊,即總計拐兩次彎的點入隊。以此類推,直到將所有的拐一次彎的點全部出隊。

3.      同理所有的可以走到的點都會入隊出隊,直到找到終點。

在此原理的基礎上,只要找到了終點,那就是最短的拐彎次數。

 

         可是做的時候並沒有去思考過多,上來就用了特別樸素的方法,也特別複雜。換了好幾次嘗試,從不用指針實現的隊到用指針實現的隊,最後又換回了不用指針實現的隊。

首先 

int DIR[4][2] ={{-1,0},{0,1},{1,0},{0,-1}};   //正點方向順時針 上右下左

void BFS(Node start, Node end) {
	Node t1;
	enqueue(start); // 將起始點進隊
	int newValue = 0;
	int i;
	int dx = 0, dy = 0;

	while(!isEmpty()) {  // 如果隊不爲空
		t1 = dequeue();  // 出隊
		for(i=0; i<4; i++)  //掃描當前節點的4個方向
		{
			dx = t1.x+DIR[i][0];
			dy = t1.y+DIR[i][1];
			if(dx == end.x && dy == end.y)
			{
				if(t1.dir != i)   //如果當前節點方向與即將要前進的方向(到終點)不一致,則拐彎數+1
				{
					Answer = Answer<t1.value+1?Answer:t1.value+1;  //比較新的路徑的拐彎數和當前所記錄的最小值,取小的數
				} else {
					Answer  = Answer<t1.value?Answer:t1.value;
				}
#ifdef TEST
				cout<<"find end!:"<<dx<<","<<dy<<", Answer = "<<Answer<<endl;
				printOMap();
#endif
				continue;  // 終點不進隊,所以用continue
			}
			if(dx>=0 && dy>=0 && dx<N && dy<M && map[dx][dy].d=='0')  // '0'表示可走的路
			{
				if(t1.dir != i)           //如果當前節點方向與即將要前進的方向不一致,則拐彎數+1
				{
					newValue = t1.value+1;
				}else {
					newValue = t1.value;
				}
				if(map[dx][dy].dir == -1 || newValue<=map[dx][dy].value) {   //如果 要判斷的點是從沒訪問過的點  或者  已經訪問過但是新的路徑拐彎次數少於之前保存的拐彎次數   
					map[dx][dy].dir = i;   // 將所判斷的點的方向置爲當前行進方向
					map[dx][dy].value = newValue;   // 將所判斷的點的拐彎書置爲新的路徑拐彎次數
					enqueue(map[dx][dy]);   // 進隊  這裏本來是要用指針來實現的,但是因爲本題設計到拐彎數和方向兩個變量調控的原因,
										    // 只保存一個值的話會有問題。除非添加多個方向的變量,否則最好是使用拷貝的方式進隊
				}
			}
		}
	}
}

我是那種建模想象能力不太好的人,一般都需要畫出圖來理解,所以我test的方式是把整個map當前的狀態繪製了出來。

void printOMap()
{
	cout<<"---------------------N="<<N<<",M="<<M<<endl;
	int i,j;
	for(i=0; i<N; i++)
	{
		for(j=0; j<M; j++)
		{
			if(map[i][j].value == BMAX)
			{	cout<<"*"<<" ";
			}else{
		        cout<<map[i][j].value<<" ";
			}
		}
		cout<<endl;
	}
}


7 7   // 7列7行

1 2 7 5  // start end

1111111  // 1是牆,0是路

0000011

1011001

1011100

1011110

1000000

1111111

find end!:4,6, Answer = 5

---------------------N=7,M=7

* * * * * * *

1 0 0 0 0 * *

* 1 * * 1 2 *

* 1 * * * 3 4

* 1 * * * * -1

* 1 2 2 2 * *

* * * * * * *

find end!:4,6, Answer = 3

---------------------N=7,M=7

* * * * * * *

1 0 0 0 0 * *

* 1 * * 1 2 *

* 1 * * * 3 4

* 1 * * * * -1

* 1 2 2 2 2 2

* * * * * * *

因此最小的拐彎數就是3了。

       在做這道題的時候,我曾遇到一個百思難得其解的問題。就是小的數據運算結果都是OK的,但是一碰到大的數據的時候,DFS後的數組輸出就有問題了。



100x120的數據爲什麼輸出變成了7行1列呢。研究了半天沒整明白。最後在老大的幫助下,發現原來我的循環隊列寫成了順序隊列,40000的長度都沒打住,把N和M的空間給踩了,可見這複雜的。也由此暴露出來了一個問題,這基礎多不牢固能犯這錯誤,哈哈。

現在來說說隊列怎麼寫吧。

typedef struct {
	int x;
	int y;
	int dir;
	int value;
	char d;
}Node;  // 節點結構體
Node map[MAX][MAX];  // 節點數組用於存儲map

typedef struct {
	Node data[LENGTH];
	int rear;
	int front;
}Queue;  // 隊列結構體
Queue que;   // 隊列

Node map[MAX][MAX];  輸入是char型的字符,那麼怎麼存儲成Node的數組呢

如果是char map[MAX][MAX],那麼你可以這樣

for(i=0; i<N; i++) {

cin>>map[i];

}

然而Node型的你則需要逐個地輸入了。

for(i=0; i<N; i++)
{
    for(j=0; j<M; j++)
    {
        cin>>map[i][j].d;
        map[i][j].x = i;
        map[i][j].y = j;
        map[i][j].dir = -1;
        map[i][j].value = 999999;
    }
}

隊列一般需要隊空、隊滿、入隊、出隊、獲取對頭、初始化等方法。

bool isEmpty()
{
    if(que.rear == que.front){
        return true;
    }else {
        return false;
    }
}

bool isFull()
{
    if((que.rear+1)%LENGTH == que.front){
        return true;
    }else {
        return false;
    }
}

void enqueue(Node n)
{
#ifdef QLOG
    cout<<"enqueue:"<<n.x<<","<<n.y<<",dir = "<<n.dir<<",value = "<<n.value<<endl;
#endif
    if(!isFull())
    {
        que.data[que.rear] = n;
        que.rear = (que.rear+1)%LENGTH;
        // que.rear++;   // 順序隊列
    } else {    
#ifdef QLOG
    cout<<"enqueue fail because queue is full"<<endl;
#endif
    }
    
}

Node dequeue()
{
    Node n = que.data[que.front];
    if(!isEmpty())
    {
        que.front = (que.front+1)%LENGTH;
        //que.front++;  // 順序隊列
#ifdef QLOG
        cout<<"dequeue:"<<n.x<<","<<n.y<<",dir = "<<n.dir<<",n.value = "<<n.value<<endl;
#endif
    } else {
#ifdef QLOG
        cout<<"Queue is empty"<<endl;
#endif
    }
    return n;
}

Node getTop()
{
    Node n = que.data[que.front];
#ifdef QLOG
    cout<<"getTop:"<<n.x<<","<<n.y<<",dir = "<<n.dir<<endl;
#endif
    return n;
}

void initQueue()
{
    que.front = que.rear = 0;
}

到此基本上這個問題就說完了。





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