goj 扫雷(dfs)

Problem Description:

扫雷这个游戏想必各位都是会玩的吧。简单说一下规则,n行m列的格子地图上有分布一些雷。起先每个格子
都是未点开的状态,我们依次点开格子,找出所有雷。有如下3条规则:
1.点开的地方如果相邻的格子(上、下、左、右,左上、左下、右上、右下)有雷,该格子显示一个数字,表
示该格子相邻的地方有几个雷(最多8)。
2.如果点开的地方是雷,则游戏结束。
3.如果点开的地方和相邻的地方都没雷,就自动向外扩张到相邻有雷的地方。
ps:没玩过扫雷不能理解上面说的可以开一下系统自带扫雷试试。不过我觉得我语言表达能力还是可以的,
你们应该能看懂。
给出一副n行m列的地图,'*'号表示雷。'.'表示非雷。
写一个程序找出,最少需要点几下就能找出所有的雷。

Input:

输入包含多组测试,每组测试是一个n行m列的地图(0<n,m<=100),地图只包含'*'或'.'。

Output:

对于每组测试,输出最少需要点几下就能找出所有的雷。

Sample Input:

5 5
...**
**.**
*****
.....
*.*.*

Sample Output:

11
解题思路:算是对扫雷的规则有了深刻的认识,典型的dfs。思路:如果点击的某个位置(x,y)是空白并且周围(八个方向)都没有雷,那么就会沿着周围按这个规则递归扩展空白,扩展过程中若遇到某个周围有雷的位置,则不再递归扩展,直接显示出该位置上的数字即可。于是我们先dfs扩展那些能扩展的位置,然后再单独点一下剩下那些有数字的位置即可。显然,这样的点击次数是最少的。
AC代码:
 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 typedef long long LL;
 4 const int maxn=105;
 5 int n,m,ans,dir[8][2]={{-1,-1},{-1,1},{1,-1},{1,1},{-1,0},{1,0},{0,-1},{0,1}};
 6 char mp[maxn][maxn];bool vis[maxn][maxn];
 7 bool is_border(int x,int y){///判断是否越界
 8     if(x>=0&&x<n&&y>=0&&y<m)return true;
 9     return false;
10 }
11 bool check(int x,int y){///检查点(x,y)周围是否都不是雷
12     for(int i=0;i<8;++i){
13         int nx=x+dir[i][0],ny=y+dir[i][1];
14         if(is_border(nx,ny)&&mp[nx][ny]=='*')return false;
15     }
16     return true;
17 }
18 void dfs(int x,int y){///不必考虑mp[x][y]=='*',因为递归前已都将是雷的排除在外,但是已经访问的要返回,减少递归次数
19     if(vis[x][y])return;///已访问则直接返回
20     vis[x][y]=true;
21     for(int i=0;i<8;++i){
22         int nx=x+dir[i][0],ny=y+dir[i][1];
23         if(is_border(nx,ny)&&!vis[nx][ny]&&mp[nx][ny]=='.'){
24             if(check(nx,ny))dfs(nx,ny);///周围没有雷,则继续dfs扩展能到达的地方
25             else vis[nx][ny]=true;///否则直接标记为T,表示最多只能扩充到当前位置
26         }
27     }
28 }
29 int main(){
30     while(~scanf("%d%d",&n,&m)){
31         memset(vis,false,sizeof(vis));ans=0;
32         for(int i=0;i<n;++i)scanf("%s",mp[i]);
33         for(int i=0;i<n;++i)
34             for(int j=0;j<m;++j)
35                 if(!vis[i][j]&&mp[i][j]=='.'&&check(i,j))dfs(i,j),ans++;///先递归扩展空白处
36         for(int i=0;i<n;++i)
37             for(int j=0;j<m;++j)
38                 if(!vis[i][j]&&mp[i][j]=='.')ans++;///再将未访问点一下即可
39         printf("%d
",ans);
40     }
41     return 0;
42 }
 
原文地址:https://www.cnblogs.com/acgoto/p/10047547.html