2019第十屆藍橋杯B組C++省賽E題 迷宮
題目連結 https://www.lanqiao.cn/problems/602/learning/
廣度優先搜尋最短路徑輸出表示
(唉。。。。。。。。。。。)
此題有三種方法:①搜兩遍,第一遍算最短路徑,第二遍輸出路徑(也是本篇部落格使用的方法);②搜一遍,一邊搜一遍記錄路徑(我不會);③搜一遍,用二維陣列儲存四個值(我更不會)。
(詳情見https://blog.csdn.net/weixin_44965308/article/details/104928695)
先說一下我錯誤的思路:先正著從起點開始搜一遍,求出每個點到起點的最短距離,再正著搜一遍,從起點往終點走,找比上一個大1的點,找到後記錄路徑,最後輸出路徑。
是這樣的,這樣是錯誤的,至於他為什麼錯,很難過,搞了兩天也沒搞懂。
接下來說一下正確的思路:先倒著從終點開始搜一遍,求出每個點到終點的最短距離,再正著搜一遍,從起點往終點走,找比上一個小1的點,找到後記錄路徑,最後輸出路徑。
為什麼第一遍不能正著搜?有個dalao的解釋是這樣的:
回溯的時候有這種請況在dis[x][y]=147的時候下一步有兩個點可以走都是最短的。因為迷宮裡最短路徑並非唯一,BFS可以求的最短路徑沒錯,但得到的與題目要求的字典序最小可能不一樣。有人說,按DLRU順序走就可以了,但是,是開始標記dis陣列時按著這順序,還是回溯時按這個順序呢?
答案是如果開始標記dis陣列時是倒著搜時,然後回溯時按這個順序是對的,但如果開始時正著搜的,那麼回溯時就不能按這個順序了,因為這樣得到的路徑是從(29,49)到(0,0)字典序最小的最短路徑而不是從(0,0)到(29,49)字典序最小的最短路徑。(原文連結
好吧......我不能理解為什麼變成是“從(0,0)到(29,49)字典序最小的最短路徑”.....
放一個倒搜的半懂不懂的程式碼:
1 #include <bits/stdc++.h> 2 using namespace std; 3 char a[40][60];//存圖 4 int dis[40][60];//存放各點到終點的距離,亦有類似於標記的作用 5 int dx[4]={1,0,0,-1};//字典序直接把方向陣列處理好 6 int dy[4]={0,-1,1,0}; 7char ch[4]={'D','L','R','U'}; 8 string st;//存答案 9 struct node 10 { 11 int x; 12 int y; 13 }; 14 bool check(int x, int y) 15 { 16 return x>0&&y>0&&x<=30&&y<=50&&a[x][y]=='0'&&dis[x][y]==-1; 17 } 18 void bfs(int bx,int by) 19 {//第一遍搜尋求出各點到終點的最短距離 20 memset(dis,-1,sizeof(dis)); 21 queue<node>q; 22 node start,next; 23 start.x=bx; 24 start.y=by; 25 q.push(start); 26 dis[30][50]=0; 27 while(!q.empty()) 28 { 29 start=q.front(); 30 q.pop(); 31 for(register int i=0;i<4;i++) 32 { 33 next.x=start.x+dx[i]; 34 next.y=start.y+dy[i]; 35 if(check(next.x,next.y)) 36 { 37 dis[next.x][next.y]=dis[start.x][start.y]+1; 38 q.push(next); 39 } 40 } 41 } 42 } 43 int main() 44 { 45 for(int i=1;i<=30;i++) 46 for(int j=1;j<=50;j++) 47 cin>>a[i][j]; 48 bfs(30,50); 49 int x=1,y=1;//從起點開始遍歷 50 while(x!=30||y!=50) 51 { 52 for(int i=0;i<4;i++) 53 { 54 int newx=x+dx[i]; 55 int newy=y+dy[i]; 56 if(newx>0&&newx<=30&&newy>0&&newy<=50&&a[newx][newy]=='0'&&dis[newx][newy]==dis[x][y]-1) 57 { 58 x=newx; y=newy; 59 st+=ch[i]; 60 break; 61 } 62 } 63 } 64 cout<<st<<endl; 65 return 0; 66 }
這是我錯誤思路的程式碼:(不重要!劃掉!)
(第26、48、56行不同)
1 #include <bits/stdc++.h> 2 using namespace std; 3 char a[40][60];//存圖 4 int dis[40][60];//存放各點到終點的距離,亦有類似於標記的作用 5 int dx[4]={-1,0,0,1};//字典序直接把方向陣列處理好 6 int dy[4]={0,1,-1,0}; 7 char ch[4]={'D','L','R','U'}; 8 string st;//存答案 9 struct node 10 { 11 int x; 12 int y; 13 }; 14 bool check(int x, int y) 15 { 16 return x>0&&y>0&&x<=30&&y<=50&&a[x][y]=='0'&&dis[x][y]==-1; 17 } 18 void bfs(int bx,int by) 19 {//第一遍搜尋求出各點到終點的最短距離 20 memset(dis,-1,sizeof(dis)); 21 queue<node>q; 22 node start,next; 23 start.x=bx; 24 start.y=by; 25 q.push(start); 26 dis[1][1]=0; 27 while(!q.empty()) 28 { 29 start=q.front(); 30 q.pop(); 31 for(register int i=0;i<4;i++) 32 { 33 next.x=start.x+dx[i]; 34 next.y=start.y+dy[i]; 35 if(check(next.x,next.y)) 36 { 37 dis[next.x][next.y]=dis[start.x][start.y]+1; 38 q.push(next); 39 } 40 } 41 } 42 } 43 int main() 44 { 45 for(int i=1;i<=30;i++) 46 for(int j=1;j<=50;j++) 47 cin>>a[i][j]; 48 bfs(1,1); 49 int x=1,y=1;//從起點開始遍歷 50 while(x!=30||y!=50) 51 { 52 for(int i=0;i<4;i++) 53 { 54 int newx=x+dx[i]; 55 int newy=y+dy[i]; 56 if(newx>0&&newx<=30&&newy>0&&newy<=50&&a[newx][newy]=='0'&&dis[newx][newy]==dis[x][y]+1) 57 { 58 x=newx; y=newy; 59 st+=ch[i]; 60 break; 61 } 62 } 63 } 64 cout<<st<<endl; 65 return 0; 66 }
好心累,dalao的講解看不懂,lqs和cjh講的也不理解。。。。。。5555555,等問過老師再來更新吧。