佇列,廣度搜尋-ZOJ 1148 The Game (BFS)
大致題意:
有一個BOARD上有很多很多相同大小的卡片。有的位置是空的,有的位置放了卡片。問卡片a和卡片b能不能用橫縱直線段連起來,如果能求連線最少由多少段組成,連線可以暫時離開BOARD。
理解
1、題目是想要得到連線最少由多少段組成,我們可以理解為連線的轉彎個數+1。此處千萬不要與路徑最短等同!
2、此題有點像尋找類似“連連看”的路徑,但是並不等同,連連看只允許轉2個彎,也就是隻能有3段。但是此處是無論轉多少次都可以。
思路
1、資料結構採用二維陣列。
由於連線可以暫時離開BOARD,表示BOARD之外也可以走。直接給二維陣列的行和列各自+2就行了,最上面、下面、左面、右面都放空格‘ ’,表示可以走。也就是說,實際我們使用的陣列的大小為(m+2)*(n+2)
2、關於路徑的搜尋,我們採用廣度搜尋。
也就是從初始點開始(上下左右四個方向搜尋),我們先搜尋完只有1個線段的,然後再搜尋只有2個線段的,再搜尋只有3個線段的,以此類推。這樣就能夠保證,一旦搜尋到了,返回的資料一定是線段數最少的。
搜尋1個線段的時候比較容易思考,就是隻要遍歷完初始點的四個方向。
那麼搜尋2個線段如何實現呢?
當我們在搜尋1個線段的時候,我們把搜尋的每一個點都存起來,然後我們按照順序對其中的點執行一樣的操作——搜尋那個點的上下左右四個方向(只有一個線段的情況)。這樣,相對於初始點而言,就是隻有2個線段的情況了。
3、那麼如何儲存這些點呢?很容易就可以想到,用佇列。
4、題目中還有容易讓人想到的問題就是,如何保證不進入死迴圈,讓判斷效率更高。
這點其實在真正實現的時候,是自然而然就解決的問題。
我們可以再使用一個二維陣列來儲存每個點的走過的情況(在筆者demo中直接是存放該點屬於第幾層的線段),如果一個點已經走過了,那麼在判斷的時候就直接過濾。
讀者可能疑問,直接過濾不會導致漏掉一些路徑嗎?
讓我們再次回顧一下我們遍歷的過程,我們是先判斷只有1個線段的路徑是否可行,再判斷2個線段,3個線段,以此類推。
假設,讓我在判斷2個線段中的時候,發現有一個點已經走過了,那麼必然是1個線段的時候,就已經放入佇列了。那麼,倘若我再次放入佇列,我最終所輸出的線段個數,必然1個線段的時候放入佇列的那個值大1,那麼也就沒有必要進行判斷了。
程式碼:
#include<cstdio>
#include<iostream>
#include<queue>
#include<cstring>
using namespace std;
//node用來記錄遍歷的x和y,放入佇列用
struct node
{
int x,y;
};
//fx與fy方便朝四個方向遍歷
int fx[]= {-1,1,0,0};
int fy[]= {0,0,-1,1};
char a[80][80]; //a用來存放使用者輸入的X和空格
int step[80][80]; //step是重點,表示記錄的該點屬於第幾段(轉彎個數+1),為-1的時候表示沒有走過。
int n,m; //n為行,m為列
int bfs(int sx,int sy,int ex,int ey)
{
//建立佇列,並把第一個點的位置放入佇列
queue<node> q;
node s;
s.x = sx;
s.y = sy;
q.push(s);
step[sx][sy] = 0;
a[sx][sy] = ' ';
a[ex][ey] = ' ';
//cur表示佇列中當前位置,t主要用來將新的node放入佇列
node Front,cur,t;
//只要佇列不為空,就會一直遍歷
//有兩種可能,一是到達了終點,跳出while。另一種是佇列遍歷結束,無法到達終點。
while(!q.empty())
{
Front = q.front();//從第一個點開始檢視,先記錄第一個點的位置
if(Front.x == ex && Front.y == ey)
return step[ex][ey];
//檢視這個點的四個方向
for(int i=0; i<4; i++)
{
cur = Front;
t.x = cur.x+fx[i];
t.y = cur.y+fy[i];
//這個while表示會朝同一個方向一致檢視,直到這個方向不能走了
//x與y不能越界,該位置上不能有卡片,並且該位置沒有走過
while(t.x>=0 && t.x<=n+1 && t.y>=0 && t.y<=m+1 && a[t.x][t.y]==' ' && step[t.x][t.y]==-1)
{
//在Front的步數基礎上+1,,表示這個
step[t.x][t.y] = step[Front.x][Front.y]+1;
q.push(t);
cur = t;
t.x = cur.x+fx[i];
t.y = cur.y+fy[i];
}
}
//由於這個點已經遍歷完了,就把它彈出佇列
q.pop();
}
return -1;
}
int main()
{
int count1=1;
while(scanf("%d%d",&m,&n)!=EOF) //n行m列
{
if(m==0 && n==0)
break;
else
printf("Board #%d:\n",count1++);;
getchar();
//用來放卡片的位置實際是1~m列和1~n行。
//0與m列,0與n行由於在題目中都時可用走的,所以都用來放空格‘ ’。
for(int i=1; i<=n; i++)
gets(a[i]+1);
for(int i=0; i<=m+1; i++)
{
a[0][i]=' ';
a[n+1][i]=' ';
a[i][0]=' ';
a[i][m+1]=' ';
}
int count2=1;
int sx,sy,ex,ey;//sx,sy為起始點位置,ex,ey為終點位置
while(scanf("%d%d%d%d",&sy,&sx,&ey,&ex)!=EOF)
{
if(sx==0 && sy==0 && ex==0 && ey==0)
break;
else
printf("Pair %d: ",count2++);
//step表示記錄的該點的轉彎的個數,先設定為-1表示沒有走過。
memset(step,-1,sizeof(step));
int ans = bfs(sx,sy,ex,ey);
if(ans == -1)
printf("impossible.\n");
else
printf("%d segments.\n",ans);
//如果之前已經走通,那麼起始點與
a[sx][sy] = a[ex][ey] ='X';
}
printf("\n");
}
return 0;
}
相關文章
- BFS廣度優先搜尋(3)--poj2251(zoj1940)(基礎題)
- 【演算法】廣度/寬度優先搜尋(BFS)演算法
- 廣度優先搜尋,分支限界- ZOJ - 1136 Multiple
- 演算法競賽——BFS廣度優先搜尋演算法
- 廣度優先搜尋(BFS)思路及演算法分析演算法
- 基本演算法——深度優先搜尋(DFS)和廣度優先搜尋(BFS)演算法
- BFS-圖的廣度優先搜尋--鄰接矩陣矩陣
- BFS廣度優先搜尋(10)--fzu2150(基礎題)
- BFS廣度優先搜尋(6)--poj3414(基礎題)
- BFS廣度優先搜尋(11)--hdu2102(基礎題)
- BFS廣度優先搜尋(4)--hdu2717(poj3278)(基礎題)
- 0演算法基礎學演算法 搜尋篇第二講 BFS廣度優先搜尋的思想演算法
- c++ 廣度優先搜尋(寬搜)C++
- js版本的(廣、深)度優先搜尋JS
- 演算法筆記(廣度優先搜尋)演算法筆記
- 深度和廣度優先搜尋演算法演算法
- 【程式碼隨想錄】廣度優先搜尋
- python 二叉樹深度優先搜尋和廣度優先搜尋Python二叉樹
- 圖的廣度優先搜尋和深度優先搜尋Python實現Python
- BFS廣度優先搜尋(5)(亦可以用DFS)--hdu1241(poj1562)(基礎題)
- hdu5040 優先佇列+bfs佇列
- 堆疊、佇列、樹、圖、搜尋 基礎知識佇列
- 「Golang成長之路」迷宮的廣度優先搜尋Golang
- NO GAME NO LIFE(優先佇列/最小堆)GAM佇列
- 啟發式搜尋的方式(深度優先,廣度優先)和 搜尋方法(Dijkstra‘s演算法,代價一致搜尋,貪心搜尋 ,A星搜尋)演算法
- golang學習筆記——迷宮的廣度優先搜尋Golang筆記
- JAVA圖搜尋演算法之DFS-BFSJava演算法
- (BFS廣度優先演算法) 油田問題演算法
- 【BFS+優先佇列】HDU 3442 Three Kingdoms佇列
- acmdream1191 bfs+優先佇列ACM佇列
- 演算法(三):圖解廣度優先搜尋演算法演算法圖解
- Swift 演算法實戰之路:深度和廣度優先搜尋Swift演算法
- 《圖論》——廣度優先遍歷演算法(BFS)圖論演算法
- UVALive 6665 Dragon’s Cruller (BFS + 優先佇列+hash)Go佇列
- 搜狗搜尋推廣平臺下線
- 基於迴圈佇列的BFS的原理及實現佇列
- 使用Google百度等搜尋引擎的常用搜尋技巧Go
- DFS與BFS——理解簡單搜尋(中文虛擬碼+例題)