当前位置:   article > 正文

BFS的基本应用——flood fill && 最短路

BFS的基本应用——flood fill && 最短路

bfs的核心就是一层一层往外扩展,在bfs中会用到一个队列,又由于是一层一层往外扩展的,故而可以用来求连通的问题,同时相当于每次往外扩展的边权都是1,所以这个队列是有序的,相当于dijkstra算法中的优先队列,那么也可以用来求边权为1的最短路问题。

Flood Fill

Flood Fill问题实际就是连通块问题,可以求二维图中连通块的数量,连通块的面积,连通块与非连通块之间的关系,连通块的边界、周长等问题。核心就是在在搜到非连通位置时的不同处理。

另外连通分四连通和八连通,四连通可以用向量来解决,而八连通可以直接围绕当前访问的点访问一个3*3的矩形,然后将中间那一块儿扣掉,当然向量也可以。

另外用到的队列既可以用queue,也可以用数组来模拟。

1097. 池塘计数(活动 - AcWing

  1. #include<bits/stdc++.h>
  2. using namespace std;
  3. #define x first
  4. #define y second
  5. typedef pair<int,int> pii;
  6. pii q[1000010];
  7. int hh,tt;
  8. int st[1010][1010];
  9. int n,m;
  10. char s[1010][1010];
  11. void bfs(int a,int b)
  12. {
  13. st[a][b]=1;
  14. hh=tt=0;
  15. q[tt++]={a,b};
  16. while(hh<tt)
  17. {
  18. auto t=q[hh++];
  19. for(int i=t.x-1;i<=t.x+1;i++)
  20. {
  21. for(int j=t.y-1;j<=t.y+1;j++)
  22. {
  23. if(i==t.x&&j==t.y) continue;
  24. if(i<1||i>n||j<1||j>m) continue;
  25. if(s[i][j]!='W'||st[i][j]) continue;
  26. q[tt++]={i,j};
  27. st[i][j]=1;
  28. }
  29. }
  30. }
  31. }
  32. int main()
  33. {
  34. scanf("%d%d",&n,&m);
  35. for(int i=1;i<=n;i++) scanf("%s",s[i]+1);
  36. int c=0;
  37. for(int i=1;i<=n;i++)
  38. {
  39. for(int j=1;j<=m;j++)
  40. {
  41. if(!st[i][j]&&s[i][j]=='W')
  42. {
  43. c++;
  44. bfs(i,j);
  45. }
  46. }
  47. }
  48. cout<<c;
  49. }

 1098. 城堡问题(活动 - AcWing

 

思路:这题看上去比较麻烦,因为它不是相邻的格子有阻碍,而是用一个数来表示当前位置四周的情况,而且这里还需要求面积。我们先来看它给定的数1,2,4,8,显然就是二进制数的某一位,那么我们实际上可以直接bfs,用该点上的数来判断将要延伸的位置是否可以达到就好。另外面积也很简单,给bfs加个返回值就好。

 

  1. #include<bits/stdc++.h>
  2. using namespace std;
  3. #define x first
  4. #define y second
  5. typedef pair<int,int> pii;
  6. pii q[2500];
  7. int hh,tt;
  8. int n,m;
  9. int st[100][100];
  10. int g[100][100];
  11. int dx[]={0,-1,0,1};
  12. int dy[]={-1,0,1,0};
  13. int bfs(int a,int b)
  14. {
  15. st[a][b]=1;
  16. hh=tt=0;
  17. q[tt++]={a,b};
  18. int s=0;
  19. while(hh<tt)
  20. {
  21. auto t=q[hh++];
  22. s++;
  23. for(int i=0;i<4;i++)//0
  24. {
  25. int nx=t.x+dx[i],ny=t.y+dy[i];
  26. if(nx<1||nx>n||ny<1||ny>m) continue;
  27. if(st[nx][ny]) continue;
  28. if(g[t.x][t.y]>>i&1) continue;//说明有墙
  29. q[tt++]={nx,ny};
  30. st[nx][ny]=1;
  31. }
  32. }
  33. return s;
  34. }
  35. int main()
  36. {
  37. scanf("%d%d",&n,&m);
  38. for(int i=1;i<=n;i++)
  39. {
  40. for(int j=1;j<=m;j++)
  41. {
  42. scanf("%d",&g[i][j]);
  43. }
  44. }
  45. int mx=0,c=0;
  46. for(int i=1;i<=n;i++)
  47. {
  48. for(int j=1;j<=m;j++)
  49. {
  50. if(!st[i][j])
  51. {
  52. c++;
  53. int tmp=bfs(i,j);
  54. mx=max(mx,tmp);
  55. }
  56. }
  57. }
  58. cout<<c<<endl<<mx;
  59. }

1106. 山峰和山谷(活动 - AcWing)

思路:这里就涉及到对边界值的处理,我们只需要在访问到边界的时候判断一下非连通块位置的值,与连通块之间的关系即可。因为要记录一下情况,所以我们多传两个参数进函数即可,另外一定要注意,这里需要传地址,不能只传值,只有数组传值可以进行改变。

  1. #include<bits/stdc++.h>
  2. using namespace std;
  3. #define x first
  4. #define y second
  5. typedef pair<int,int> pii;
  6. pii q[1000010];
  7. int st[1010][1010];
  8. int g[1010][1010];
  9. int n,hh,tt;
  10. void bfs(int a,int b,int&h,int&v)
  11. {
  12. st[a][b]=1;
  13. hh=tt=0;
  14. q[tt++]={a,b};
  15. while(hh<tt)
  16. {
  17. auto t=q[hh++];
  18. for(int i=t.x-1;i<=t.x+1;i++)
  19. {
  20. for(int j=t.y-1;j<=t.y+1;j++)
  21. {
  22. if(i==t.x&&j==t.y) continue;
  23. if(i<1||i>n||j<1||j>n) continue;
  24. if(g[i][j]>g[t.x][t.y]) h++;
  25. else if(g[i][j]<g[t.x][t.y]) v++;
  26. else
  27. {
  28. if(st[i][j]) continue;//因为要看非连通位置,所以在加入队列之前再判断是否出现过
  29. q[tt++]={i,j};
  30. st[i][j]=1;
  31. }
  32. }
  33. }
  34. }
  35. }
  36. int main()
  37. {
  38. scanf("%d",&n);
  39. for(int i=1;i<=n;i++)
  40. for(int j=1;j<=n;j++)
  41. scanf("%d",&g[i][j]);
  42. int sf=0,sg=0;
  43. for(int i=1;i<=n;i++)
  44. {
  45. for(int j=1;j<=n;j++)
  46. {
  47. if(!st[i][j])
  48. {
  49. int h=0,v=0;
  50. bfs(i,j,h,v);
  51. if(h&&v) continue;
  52. else if(v) sf++;//有比它矮的
  53. else if(h) sg++;//有比它高的
  54. else sf++,sg++;
  55. }
  56. }
  57. }
  58. cout<<sf<<" "<<sg;
  59. }

最短路问题

这里的最短路问题有个条件就是每一步的权重都相等(未必都是1,只要相等即可),根据bfs中队列的性质,每个位置第一次被扫到的时候,那么进行的操作数或者走过的路径就是最短路。

1076. 迷宫问题(活动 - AcWing

 

思路:这里输出最短路,我们的处理就是记录每个位置是由谁转移而来的。相当于将st数组变成pair<int,int>的类型,我们因为这里的左边是从0开始的,那么我们可以将st初始化成-1,memset(st,-1,sizeof st),那么每个st的第一位和第二位就是-1,我们查找的时候只用判断一下就知道这个位置是否已经被搜过了。另外免得再存一遍,我们可以从终点往起点搜。

而且这里的搜索也不再是遍历了,而是从某一个点开始搜。

  1. #include<bits/stdc++.h>
  2. using namespace std;
  3. #define x first
  4. #define y second
  5. typedef pair<int,int> pii;
  6. pii q[1000010];
  7. pii st[1010][1010];
  8. int hh,tt;
  9. int n;
  10. int g[1010][1010];
  11. int dx[]={0,0,1,-1};
  12. int dy[]={1,-1,0,0};
  13. void bfs(int a,int b)
  14. {
  15. memset(st,-1,sizeof st);
  16. st[a][b]={a,b};
  17. hh=tt=0;
  18. q[tt++]={a,b};
  19. while(hh<tt)
  20. {
  21. auto t=q[hh++];
  22. for(int i=0;i<4;i++)
  23. {
  24. int nx=t.x+dx[i],ny=t.y+dy[i];
  25. if(nx<0||nx>=n||ny<0||ny>=n) continue;
  26. if(st[nx][ny].x!=-1) continue;
  27. if(g[nx][ny]==1) continue;
  28. st[nx][ny]={t.x,t.y};
  29. q[tt++]={nx,ny};
  30. }
  31. }
  32. }
  33. int main()
  34. {
  35. scanf("%d",&n);
  36. for(int i=0;i<n;i++)
  37. for(int j=0;j<n;j++)
  38. scanf("%d",&g[i][j]);
  39. bfs(n-1,n-1);
  40. pii e(0,0);//表示定义一个名为e的pair<int,int>类型的变量,初值赋为{0,0}
  41. while(1)
  42. {
  43. cout<<e.x<<" "<<e.y<<endl;
  44. if(e.x==n-1&&e.y==n-1) break;
  45. e=st[e.x][e.y];
  46. }
  47. }

188. 武士风度的牛(188. 武士风度的牛 - AcWing题库)

 

思路:就相当于马走日,从一个点到另一个点最少多少步,那么就是一个bfs宽搜问题。 不过这里需要注意的是,中间有障碍物可能有些位置需要判断一下。

  1. #include<bits/stdc++.h>
  2. using namespace std;
  3. #define x first
  4. #define y second
  5. typedef pair<int,int> pii;
  6. pii q[40010];
  7. int n,m,hh,tt;
  8. char s[200][200];
  9. int ex,ey;
  10. int d[200][200];
  11. int st[200][200];
  12. int dx[]={1,1,-1,-1,2,2,-2,-2};
  13. int dy[]={2,-2,2,-2,1,-1,1,-1};
  14. void bfs(int sx,int sy)
  15. {
  16. memset(d,0x3f,sizeof d);
  17. d[sx][sy]=0;
  18. st[sx][sy]=1;
  19. hh=tt=0;
  20. q[tt++]={sx,sy};
  21. while(hh<tt)
  22. {
  23. auto t=q[hh++];
  24. for(int i=0;i<8;i++)
  25. {
  26. int nx=t.x+dx[i],ny=t.y+dy[i];
  27. if(nx<1||nx>n||ny<1||ny>m) continue;
  28. if(st[nx][ny]) continue;
  29. if(s[nx][ny]=='*') continue;
  30. q[tt++]={nx,ny};
  31. d[nx][ny]=d[t.x][t.y]+1;
  32. st[nx][ny]=1;
  33. }
  34. }
  35. }
  36. int main()
  37. {
  38. scanf("%d%d",&m,&n);
  39. for(int i=1;i<=n;i++) scanf("%s",s[i]+1);
  40. int sx,sy;
  41. for(int i=1;i<=n;i++)
  42. {
  43. for(int j=1;j<=m;j++)
  44. {
  45. if(s[i][j]=='K') sx=i,sy=j;
  46. if(s[i][j]=='H') ex=i,ey=j;
  47. }
  48. }
  49. bfs(sx,sy);
  50. //printf("%d %d %d %d\n",sx,sy,ex,ey);
  51. cout<<d[ex][ey];
  52. }

 ps:这题的陷阱在于先输入列数再输入行数,记得交换一下。

1100. 抓住那头牛(活动 - AcWing

这里虽然是在数轴上,但仍旧是每个点有三种操作,求到另一个点的最短路,看着有点像贪心,但是实际上就是bfs暴搜。另外要注意一点,当牛在左边时,+1和2倍的操作都没有意义了,所以右边界实际上是2e5。

  1. #include<bits/stdc++.h>
  2. using namespace std;
  3. const int N=2e5;
  4. int n,m;
  5. int d[N],st[N],q[N],hh,tt;
  6. void bfs(int s)
  7. {
  8. st[s]=1;
  9. d[s]=0;
  10. hh=tt=0;
  11. q[tt++]=s;
  12. while(hh<tt)
  13. {
  14. auto t=q[hh++];
  15. if(0<=t+1&&t+1<=N&&!st[t+1])
  16. {
  17. d[t+1]=d[t]+1;
  18. st[t+1]=1;
  19. q[tt++]=t+1;
  20. }
  21. if(0<=t-1&&t-1<=N&&!st[t-1])
  22. {
  23. d[t-1]=d[t]+1;
  24. st[t-1]=1;
  25. q[tt++]=t-1;
  26. }
  27. if(0<=2*t&&2*t<=N&&!st[2*t])
  28. {
  29. d[2*t]=d[t]+1;
  30. st[2*t]=1;
  31. q[tt++]=2*t;
  32. }
  33. }
  34. }
  35. int main()
  36. {
  37. scanf("%d%d",&n,&m);
  38. bfs(n);
  39. cout<<d[m];
  40. }

 ps:一定要注意左边界可以到0.

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/神奇cpp/article/detail/836298
推荐阅读
  

闽ICP备14008679号