可以使用BFS或者DFS方法解决的迷宫问题!
题目如下:
kotori在一个n*m迷宫里,迷宫的最外层被岩浆淹没,无法涉足,迷宫内有k个出口。kotori只能上下左右四个方向移动。她想知道有多少出口是她能到达的,最近的出口离她有多远?
输入描述:
第一行为两个整数n和m,代表迷宫的行和列数 (1≤n,m≤30)
后面紧跟着n行长度为m的字符串来描述迷宫。'k'代表kotori开始的位置,'.'代表道路,'*'代表墙壁,'e'代表出口。保证输入合法。
输出描述:
若有出口可以抵达,则输出2个整数,第一个代表kotori可选择的出口的数量,第二个代表kotori到最近的出口的步数。(注意,kotori到达出口一定会离开迷宫)
若没有出口可以抵达,则输出-1。
示例1
1 #include<iostream> 2 #include<string.h> 3 using namespace std; 4 char map[100][100]; 5 int use[100][100]; 6 int dir[4][2] = {{0,1},{1,0},{0,-1},{-1,0}}; //上下左右四个方向 7 int minn = 999999999; 8 9 int pan(int x,int y) 10 { 11 if(0 <= x && x <= 5 && 0 <= y && y <= 7 && (map[x][y] == '.' || map[x][y]=='k')) return 1; 12 else return 0; 13 } 14 15 void dfs(int x,int y,int step) 16 { 17 // 递归程序,必须要设置整个程序的出口,在dfs中,即当走到迷宫出口处即可结束程序 18 if(map[x][y] == 'e') 19 { 20 cout<<"success"<<endl; 21 if(step < minn) minn = step; 22 return; 23 } 24 for(int i = 0; i < 4; i++) 25 { 26 if(pan(x,y) && use[x][y] != 1) 27 { 28 use[x][y] = 1; 29 cout<<"dd"; 30 cout<<" "<<map[x+dir[i][0]][y+dir[i][1]]<<endl; 31 dfs(x+dir[i][0],y+dir[i][1],step+1); 32 use[x][y] = 0; 33 } 34 } 35 36 } 37 int main() 38 { 39 int n; 40 int m; 41 cin >> n >> m; 42 // 4,5行已经定义了map和use数据,所以在此处不必int map[n][m],直接map[n][m]即可,否则报错 43 map[n][m]; 44 use[n][m]; 45 memset(use,0,sizeof(use)); 46 int x_start = 0; 47 int y_start = 0; 48 int step = 0; 49 for(int i = 0; i < n; i++) 50 { 51 for(int j = 0; j < m; j++) 52 { 53 cin >> map[i][j]; 54 if(map[i][j] == 'k') 55 { 56 x_start = i; 57 y_start = j; 58 } 59 } 60 } 61 cout<<x_start<<" "<<y_start<<endl; 62 dfs(x_start, y_start, step); 63 cout<<"最小步数"<<minn<<endl; 64 }
BFS解决如下:
(1)自己写的有缺陷的代码:
1 #include<iostream> 2 #include<queue> 3 #include<string.h> 4 using namespace std; 5 char map[100][100]; 6 int vis[100][100]; 7 int use[100][100]; 8 int m,n; 9 int x_start,y_start; 10 queue<int> que; 11 int dir[4][2] = {{0,1},{0,-1},{1,0},{-1,0}}; 12 int cnt = 0; 13 int mi = 999999; 14 15 int pan(int x,int y) 16 { 17 if(0 <= x && x <= n-1 && 0 <= y && y <= m -1 &&map[x][y] != '*') return 1; 18 else return 0; 19 } 20 21 void bfs(int x,int y) 22 { 23 int x1 = x; 24 int y1 = y; 25 while(!que.empty()) 26 { 27 vis[x1][y1] = 1; 28 x1 = que.front(); 29 que.pop(); 30 y1 = que.front(); 31 que.pop(); 32 cout<<"x1:"<<x1<<" "<<"y1:"<<y1<<endl; 33 if(map[x1][y1] == 'e') 34 { 35 if(use[x1][y1] == 0) 36 { 37 cnt++; 38 use[x1][y1]=1; 39 } 40 continue; 41 } 42 43 for(int i = 0; i < 4; i++) 44 { 45 int xx = x1 + dir[i][0]; 46 int yy = y1 + dir[i][1]; 47 if(pan(xx,yy) && vis[xx][yy] == 0) 48 { 49 cout<<"dd"<<endl; 50 cout<<xx<<" "<<yy<<endl; 51 que.push(xx); 52 que.push(yy); 53 vis[xx][yy] = 1; 54 } 55 } 56 } 57 } 58 59 int main() 60 { 61 memset(vis,0,sizeof(vis)); 62 memset(use,0,sizeof(use)); 63 cin >> n >> m; 64 for(int i = 0; i < n; i++) 65 { 66 for(int j = 0; j < m; j++) 67 { 68 cin >> map[i][j]; 69 if(map[i][j] == 'k') 70 { 71 x_start = i; 72 y_start = j; 73 } 74 } 75 } 76 que.push(x_start); 77 que.push(y_start); 78 bfs(x_start,y_start); 79 cout<<cnt<<endl; 80 81 }
对于每个点每个状态我采用的是直接利用队列记录他们的坐标值,而不是如AC代码一样利用结构体记录每个点的每个状态。所以导致我整个程序还是存在很大的缺陷,例如求最短路径的时候就比较困难。
所以对于BFS的题目,强烈建议把每个点每个状态先用结构体表示,然后利用队列记录这些结构体即可。
(2)AC代码
1 #include<bits/stdc++.h> 2 using namespace std; 3 char a[35][35]; 4 bool usd[35][35]; 5 int Move[4][2]={{0,1},{1,0},{-1,0},{0,-1}}; 6 struct now 7 { 8 int x,y,dis; 9 }; 10 queue<now>q; 11 int main() 12 { 13 int n,m,cnt=0; 14 scanf("%d%d",&n,&m); 15 now s; 16 for(int i=1;i<=n;++i) 17 for(int j=1;j<=m;++j) 18 { 19 cin>>a[i][j]; 20 if(a[i][j]=='k') 21 { 22 s.x=i; 23 s.y=j; 24 s.dis=0; 25 } 26 } 27 q.push(s); 28 int ans=99999999; 29 while(!q.empty()) 30 { 31 now Now=q.front(); 32 q.pop(); 33 if(a[Now.x][Now.y]=='e') 34 { 35 if(!usd[Now.x][Now.y]) 36 { 37 ++cnt; 38 ans=min(ans,Now.dis); 39 } 40 usd[Now.x][Now.y]=true; 41 continue; //到达出口"e"处就必须跳过一下步骤,不能在对出口点“e”进行下面的扩展步骤(上下左右) 42 } 43 usd[Now.x][Now.y]=true; 44 for(int i=0;i<4;++i) 45 { 46 int xx=Now.x+Move[i][0],yy=Now.y+Move[i][1],d=Now.dis; 47 if(xx<=n&&xx>=1&&yy>=1&&yy<=m&&!usd[xx][yy]&&a[xx][yy]!='*') 48 { 49 now t; 50 t.x=xx; 51 t.y=yy; 52 t.dis=d+1; 53 q.push(t); 54 } 55 } 56 } 57 if(!cnt) 58 return !printf("-1 "); 59 printf("%d %d ",cnt,ans); 60 return 0; 61 }
带路径输出的BFS(路径的输出主要依靠递归程序,记录每个点的结构体还需要记录每个点的前驱节点的坐标)
1 #include<bits/stdc++.h> 2 using namespace std; 3 char a[35][35]; 4 bool usd[35][35]; 5 int Move[4][2]={{0,1},{1,0},{-1,0},{0,-1}}; 6 struct now 7 { 8 int x,y,dis,pre_x,pre_y; 9 }; 10 queue<now>q; 11 now buf[35]; 12 int count1 = 0; 13 14 void print(int x,int y) 15 { 16 int temp; 17 for(int i = 0; i < count1; i++) 18 { 19 if(buf[i].x == x && buf[i].y == y) temp = i; 20 } 21 if(x == -1 && y == -1) return; 22 else 23 { 24 print(buf[temp].pre_x,buf[temp].pre_y); 25 cout<<"("<<x<<","<<y<<")"<<endl; 26 } 27 } 28 29 int main() 30 { 31 int n,m,cnt=0; 32 scanf("%d%d",&n,&m); 33 now s; 34 for(int i=1;i<=n;++i) 35 for(int j=1;j<=m;++j) 36 { 37 cin>>a[i][j]; 38 if(a[i][j]=='k') 39 { 40 s.x=i; 41 s.y=j; 42 s.pre_x = -1; 43 s.pre_y = -1; 44 s.dis=0; 45 } 46 } 47 q.push(s); 48 int ans=99999999; 49 while(!q.empty()) 50 { 51 now Now=q.front(); 52 buf[count1++] = Now; 53 q.pop(); 54 if(a[Now.x][Now.y]=='e') 55 { 56 if(!usd[Now.x][Now.y]) 57 { 58 ++cnt; 59 ans=min(ans,Now.dis); 60 usd[Now.x][Now.y]=true; 61 print(Now.x,Now.y); 62 } 63 continue; 64 } 65 usd[Now.x][Now.y]=true; 66 for(int i=0;i<4;++i) 67 { 68 int xx=Now.x+Move[i][0],yy=Now.y+Move[i][1],d=Now.dis; 69 if(xx<=n&&xx>=1&&yy>=1&&yy<=m&&!usd[xx][yy]&&a[xx][yy]!='*') 70 { 71 now t; 72 t.x=xx; 73 t.y=yy; 74 t.pre_x = Now.x; 75 t.pre_y = Now.y; 76 t.dis=d+1; 77 q.push(t); 78 } 79 } 80 } 81 if(!cnt) 82 return !printf("-1 "); 83 printf("%d %d ",cnt,ans); 84 // for(int i = 0; i < count1; i++) 85 // { 86 // cout<<buf[i].x<<" "<<buf[i].y<<" "<<buf[i].pre_x<<" "<<buf[i].pre_y<<endl; 87 // } 88 return 0; 89 }
利用dfs解决最大连通块问题!
题目描述
农场主约翰的农场在最近的一场风暴中被洪水淹没,这一事实只因他的奶牛极度害怕水的消息而恶化。
然而,他的保险公司只会根据他农场最大的“湖”的大小来偿还他一笔钱。
农场表示为一个矩形网格,有N(1≤N≤100)行和M(1≤M≤100)列。网格中的每个格子要么是干的,
要么是被淹没的,而恰好有K(1≤K≤N×M)个格子是被淹没的。正如人们所期望的,一个“湖”有一个
中心格子,其他格子通过共享一条边(只有四个方向,对角线不算的意思)与之相连。任何与中央格子共享一条边或与中央格
子相连的格子共享一条边的格子都将成为湖的一部分。
输入描述:
第一行有三个整数N,M,K,分别表示这个矩形网格有N行,M列,K个被淹没的格子。
接下来K行,每一行有两个整数R,C。表示被淹没的格子在第R行,第C列。
输出描述:
输出最大的“湖”所包含的格子数目
输入
3 4 5 3 2 2 2 3 1 2 3 1 1
输出
4
1 #include<iostream> 2 using namespace std; 3 int map[100][100]; 4 int vis[100][100] = {0}; 5 int used[100][100] = {0}; 6 int dir[4][2] = {{1,0},{-1,0},{0,1},{0,-1}}; 7 int n,m,k; 8 int cnt = 1; 9 int maxx = -1; 10 int pan(int x,int y) 11 { 12 if(map[x][y] == 1 && 0 <= x && x <= n-1 && y <= m-1 && y >= 0 && vis[x][y] == 0 && used[x][y]==0) 13 { 14 return 1; 15 } 16 else return 0; 17 } 18 19 void dfs(int x,int y,int c) //连通块问题就不像迷宫问题有递归出口!!!! 20 { 21 vis[x][y] = 1; 22 // cout<<x<<" "<<y<<" dd"<<endl; 23 for(int i = 0; i < 4; i++) 24 { 25 int xx = x + dir[i][0]; 26 int yy = y + dir[i][1]; 27 if(pan(xx,yy)) 28 { 29 // cout<<xx<<" "<<yy<<endl; 30 vis[xx][yy] = 1; 31 used[xx][yy] = 1; // used数组是防止多次记录连通块! 32 cnt++; 33 c = cnt; // 这一块必须注意,不能直接传入cnt,因为递归函数是放在栈中,倘若传入cnt,递归函数出栈时,cnt的值也会变化,所以用c代替cnt. 34 dfs(xx,yy,c); 35 vis[xx][yy] = 0; 36 } 37 } 38 } 39 40 int main() 41 { 42 cin >> n >> m >> k; 43 map[n][m]; 44 for(int i = 0; i < n; i++) 45 { 46 for(int j = 0; j < m; j++) map[i][j] = 0; 47 } 48 for(int i = 0; i < k; i++) 49 { 50 int x,y; 51 cin >> x >> y; 52 map[x-1][y-1] = 1; 53 } 54 // for(int i = 0; i < n; i++) 打印整个地图 55 // { 56 // for(int j = 0; j < m; j++) 57 // { 58 // cout<<map[i][j]; 59 // } 60 // cout<<endl; 61 // } 62 for(int i = 0; i < n; i++) 63 { 64 for(int j = 0; j < m; j++) 65 { 66 cnt = 1; 67 if(map[i][j] == 1) 68 { 69 dfs(i,j,cnt); 70 if(cnt > maxx) maxx = cnt; 71 } 72 } 73 } 74 cout<<maxx<<endl; 75 76 }
本题总结:
1.利用dfs解决连通块问题与利用dfs解决迷宫问题存在一定的区别:
(1)迷宫问题有固定的入口或者出口,而连通块问题就没有所谓的出口或者入口,它需要遍历map[][]数组中的每个元素!