[ 题解 ] [ DFS ] [ 连通块 ] POJ 2386 - Lake Counting / 1979 - Red and Black

VJudge题目:https://cn.vjudge.net/contest/279018#problem/G

https://cn.vjudge.net/contest/279018#problem/I

POJ 2386 - Lake Countinghttp://poj.org/problem?id=2386

POJ 1979 - Red and Blackhttp://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};


这样一上来所有字符全是\0map[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的写法。

 

转载于:https://www.cnblogs.com/Kaidora/p/10392641.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值