VJudge题目:https://cn.vjudge.net/contest/279018#problem/G
https://cn.vjudge.net/contest/279018#problem/I
即POJ 2386 - Lake Counting:http://poj.org/problem?id=2386
POJ 1979 - Red and Black:http://poj.org/problem?id=1979
请先读题。
如果一个字符相邻四格有相同字符,表明它们连在一起,称四连通。同理有八连通。
输入一个M*N字符画:
G题问图中有多少个W八连通;
I题问图中@所在 . 四连通有多大。
简单的求连通块题目。其实也可以BFS但是毕竟DFS容易写,所以被当做经典的BFS例题。
简单解释下什么是DFS/BFS:
像这个二叉树,你要查找里面所有的节点,有哪些方法?
最基本的两种思路:遍历分支;从上到下。
1 => 10 => 100 => 101 => 11 => 110 => 111
这种先把一个分支搜索完再去另一个分支的,叫深度优先搜索/DFS,因为这二货算法不搜索到无路可走是不会回头的。
1 => 10 => 11 => 100 => 101 => 110 => 111
如果是这样,必须先把一层搜索完再进入下一层,就叫宽度优先搜索/BFS。同样的二货。
这二叉树似乎与题目没有什么关系,其实这里只是为了说明什么是DFS把树搬出来而已。
在这题目中,查找连通块同样有两种思路。比如这图:
..#.#.. ..#.#.. ###.### ...@... ###.### ..#.#.. ..#.#..
从@开始走过. :
如果是DFS,它会一直向一个方向走,然后一步一步退回来(检查是否存在分支),退回@后发现了分支,又一条路走到黑;
如果是BFS,会先把周围四格踩一遍,确定没有分支后再继续踩相邻的格子。
类似这样:
..#.#.. ..#.#.. ###7### 321@456 ###.### ..#.#.. ..#.#..
..#.#.. ..#7#.. ###3### .51@26. ###4### ..#.#.. ..#.#..
大概明白了吧。
连不连通就是坐标(数组下标)是否相差1的事,几连通就几次循环。无奈我当时还是个新手,没有循环写得超级难看。
在这其中要注意所谓越界问题,就是数组下标超出范围,诸如map[-1][2]之类。很多教材题解会弄所谓的越界检查。
不需要!如果只是四连通八连通,直接把第一行第一列让出来就行了。比如最大10*10的地图,弄个二维字符数组map:
char map[12][12]={0};
这样一上来所有字符全是\0。map[0]不要用,每行开头map[X][0]也不要用,而C/C++的字符串是以\0结尾的,这样你的地图就全被\0包围了。
00000000 0char000 00000000 00000000
搜索过程如果发现这个字符值是\0就离开,保证不越界。
万一有题目是12连通,24连通呢?让两行两列啊!反正只要保证\0的区域比它的搜索范围大就行了。
先解决简单的G题。代码如下:
1 #include <stdio.h> 2 #include <string.h> 3 int M=0,N=0,answer=0; 4 char field[102][102]={0}; 5 int dfs(int m,int n ) 6 { 7 field[m][n]='F'; 8 for(int i=-1;i<=1;i++) 9 for(int j=-1;j<=1;j++) 10 // if( i || j ) 11 if( field[m+i][n+j]=='W')dfs(m+i,n+j); 12 13 return 1; 14 } 15 int main() 16 { 17 scanf("%d%d",&M,&N); getchar(); 18 for(int m=1;m<=M;m++)gets(&field[m][1]); 19 20 for(int m=1;m<=M;m++) 21 for(int n=1;n<=N;n++) 22 if(field[m][n]=='W') 23 { 24 if( dfs(m,n) )answer++; 25 } 26 printf("%d\n",answer); 27 return 0; 28 }
函数原型大概就是gets(char *p)。平时打的gets(s),是因为数组名代表数组首地址。你把第二个字符的地址递给它就可以跳过第一个字符。其他的非常好懂。
I题不外乎就是加一个计步的要求。其实在G题的基础上,弄一个全局变量step=1,每次走过一格就+1,最后输出step就完事。没必要像下面这样写:
1 #include <stdio.h> 2 #include <string.h> 3 int W,H; 4 char room[22][22]={0}; 5 6 int dfs(int h,int w) 7 { 8 int tmpplus=0; 9 for(int i=-1;i<=1;i++) 10 for(int j=-1;j<=1;j++) 11 { 12 if( (i==0 || j==0) && i+j!=0 ) 13 if( room[h+i][w+j]=='.') 14 { 15 room[h+i][w+j]='@'; 16 tmpplus++; 17 tmpplus+=dfs(h+i,w+j); 18 } 19 } 20 return tmpplus; 21 } 22 23 int main() 24 {Start:; 25 26 int answer=1; 27 scanf("%d%d",&W,&H); 28 if(W==0)return 0; 29 30 getchar();memset(room,0,22*22*sizeof(char) ); 31 for(int h=1;h<=H;h++) 32 { 33 gets( &room[h][1] ); 34 } 35 36 for(int h=1;h<=H;h++) 37 for(int w=1;w<=W;w++) 38 { 39 if( room[h][w]=='@' ) 40 { 41 answer+=dfs(h,w); 42 } 43 } 44 printf("%d\n",answer); 45 46 goto Start; 47 }
这返回值是什么意思?它表明这个节点有多少个子节点。就像这个:
..#.#.. ..#.#.. ###7### 321@456 ###.### ..#.#.. ..#.#..
当你搜索第一格,这个tmpplus首先++,表明找到了一个子节点;
下一句dfs执行完后,返回值2,表明这一子节点有两个子/孙节点,把它加上,表明这一分支有3个点。
循环四次下来12个点就找到了,加上@本身,答案13。
至于BFS的写法,可以看另一篇题解
https://www.cnblogs.com/Kaidora/p/10392293.html
虽然不是连通块题目,但是可以参考BFS的写法。