【转自己的落谷博客】联通块的dfs
最近做了三道题,几乎用了同样的代码。
他们都是地图中的联通问题。
分别是P1596 [USACO10OCT]Lake Counting S,
CF598D Igor In the Museum,
P1141 01迷宫,
其要求都是在一个图中,寻找特定要求的连通块。
统计连通块的数目(P1596)或者每个连通块内的坐标数量(P1141)
或者连通块所接壤的非连通坐标的数目和(CF598D)。
其中图的规模均为n^2(1e6),查询的规模也是如此,
因此每次在查询时搜索并不是个省时间的好主意,所以三道题我都用了预处理,
用f[i][j]来确定每个点所拥有的数值,其中f[i][j]的含义并不相同,
但均可以在同一步骤中出现。
基本的实现方法都是dfs
用的大同小异;
分别展示dfs的部分
int o[1010][1010];//是否走过 char s[1010][1010];//坐标上的点是什么 int n,m,u; int z[8]={-1,1,0,0,1,-1,-1,1},w[8]={0,0,-1,1,1,-1,1,-1}; void search(int x,int y) { if(x==0||y==0||x>n||y>m) { return; } for(int i=0;i<8;i++) { if(s[x+z[i]][y+w[i]]=='W'&&o[x+z[i]][y+w[i]]==0) { o[x+z[i]][y+w[i]]=1; search(x+z[i],y+w[i]); } } }
这道是Lake counting S的dfs部分,其中寻找的是W的连通块,
连通的条件是经典的九 宫格,所以用了8个移动来寻找连通,
这道题寻找的是连通块的数目,所以在dfs中除了标记连通过的点,
防止再找一遍这个大块,其余不用做什么。在主函数部分计数就好。
int f[1010][1010],o[1010][1010],h[1010000],p[1010000]; char s[1010][1010]; int n,m,k,sum,u; int z[4]={-1,1,0,0},w[4]={0,0,-1,1}; void search(int x,int y) { if(x==0||y==0||x>n||y>m) { return; } for(int i=0;i<4;i++) { if(s[x+z[i]][y+w[i]]=='*') sum++; if(s[x+z[i]][y+w[i]]=='.'&&o[x+z[i]][y+w[i]]==0) { u++; h[u]=x+z[i]; p[u]=y+w[i]; o[x+z[i]][y+w[i]]=1; search(x+z[i],y+w[i]); } } }
这道是igor in the museum,图中为‘.’和‘*’
找到是每一个连通块中‘.’链接‘*’的数目之和
所以用sum来记录这个块,用u和h[u]以及p[u]来记录一个连通块中的所有点,
方便他们统一sum。主函数中这里的统一使用与调配如下。
for(int i=1;i<=n;i++) { for(int j=1;j<=m;j++) { if(s[i][j]=='.'&&o[i][j]==0) { u++; o[i][j]=1; h[u]=i; p[u]=j; search(i,j); for(int v=1;v<=u;v++) { f[h[v]][p[v]]=sum; } } sum=0;u=0; } }
最后一份01迷宫,算是上一个的简化,因为只用到了u来统计连通内点的数量,没用到sum。
int f[1010][1010],o[1010][1010],h[1010000],p[1010000]; char s[1010][1010]; int n,k,sum,u; int z[4]={-1,1,0,0},w[4]={0,0,-1,1}; void search(int x,int y) { if(x==0||y==0||x>n||y>n) { return; } for(int i=0;i<4;i++) { if(s[x+z[i]][y+w[i]]=='1'&&s[x][y]=='0'&&o[x+z[i]][y+w[i]]==0||s[x+z[i]][y+w[i]]=='0'&&s[x][y]=='1'&&o[x+z[i]][y+w[i]]==0) { u++; h[u]=x+z[i]; p[u]=y+w[i]; o[x+z[i]][y+w[i]]=1; search(x+z[i],y+w[i]); } } }
到此结束,简单的连通问题总结完成啦。