问题 H: 扫雷
时间限制: 1 Sec 内存限制: 128 MB
提交: 93 解决: 33
[提交][状态][讨论版]
题目描述
扫雷是一款系统自带的十分经典的益智游戏,以上手简单而玩法灵活而深受人们喜爱。游戏的目标是在最短的时间内找出所有的安全格子并且避免踩雷。
游戏界面如下:
游戏规则如下:
所有格子分为已知格子和未知格子两种。其中未知格子,即未确定的格子,未点开且不能判断是否为地雷或安全格子的格子;已知格子,即已确定的格子,为数字、空白、地雷、安全格子4者中的一种------数字代表此格子周围的8个格子(有时不足8个,譬如边格子5个、角格子3个)里地雷的数目,用以辅助判断周围的格子是否安全;空白代表此格子周围没有地雷;安全格子是指确定不是地雷的未点开的格子。例如代表周围8个格子里有2个地雷(插旗子处),1个安全格子(标绿点处)如图。
这里定义一个概念——局面,是指游戏进行到某个阶段时地图的状态,一旦点开某个未点开的格子(任何作标记的操作都不算),即进入下一个局面。
最近Doge也迷上了这款游戏,这里我们的问题是:求在当前局面下,Doge能发现的安全格子总数。所幸Doge是扫雷新手,他能解决的问题比较简单:Doge只会根据单个数字格子判断以此格子为中心3×3区域的格子是地雷还是安全格子。
即对于某个数字格子,
若设z=当前数字格子中显示的数字;
x=当前数字格子周围未知格子的数目;
y=当前数字格子周围已确定的地雷的数目。
则:
如果x+y=z,那么就能判定当前数字格子周围的x个未知格子全部为地雷;
如果y=z, 那么就能判定当前数字格子周围的x个未知格子全部为安全格子。
Doge虽然作为一个新手,但是这样的手法还是会的:把能确定是 地雷/安全格子 的格子做上标记,以方便其它格子的判断。
在一个局面中,Doge可做的标记操作只有两种:
1. 插旗子,表示确定该格子一定是地雷;
2. 标绿点,表示确定该格子一定是安全格子。
对于这个局面,Doge按照自己的操作,会得出以下的结论,其中插旗处是确定为地雷的格子,绿点是安全格子。
输入
第一行包含一个数字T(T <= 50),表示测试样例组数。
接下来T组测试数据。每组测试数据第一行为一个数字N(2<=N<=10),代表输入地图的长和宽。下面有N行,每行为一个包含N个字符的字符串,其中大写字母“P”代表旗子,“_”代表空白格子,“#”代表未知的格子,数字代表的意思和题意相符。输入保证符合游戏规则。
输出
每组输入对应输出一行“Case #x: y”,x表示数据所在组数,y表示在当前局面下Doge能发现的安全格子总数。
样例输入
2
8
##1_2###
#21_2###
#2__112#
#2____1#
#211__1#
###1__1#
##21__11
##1_____
4
P#1#
#32#
223#
####
样例输出
Case #1: 5
Case #2: 2
提示
这题真真切切感觉数据弱(注:感觉)
本来是想用什么DFS之类搞一下,但自己并不理清,于是真的硬生生的多次循环(7次)遍历整张表,当时只是猜测大概7次左右就能排除干净了,心想多几次循环肯定TLE,结果没打算过的题一次就AC,看了一下运行下结果22ms,我就知道这题大该是数据弱,没有起到想要考察的算法作用,就让我给水过去了,这题我也不承认会做,以后有心情会补上(或者有实力)。
//刚刚回头看了一下规模 最大为 10*10 ,难怪当时敢用多次for循环(现在看来应该开8次一个周边最稳妥,但8次并不意味真的能够全排干净【个人感觉】),22ms也在情理中.
#include <iostream>
#include <cstdio>
#include <map>
#include <climits>
#include <cstring>
#include <cstdlib>
#include <algorithm>
#include <cmath>
using namespace std;
typedef long long LL;
char A[2001][2001];
void Judge(int &x,int &y,int i,int j)
{
x=y=0;
for(int dy=-1;dy<=1;dy++)
for(int dx=-1;dx<=1;dx++)
{
if(!dx&&!dy) continue;//本身不动
if(A[i+dy][j+dx]=='P') y++;
if(A[i+dy][j+dx]=='#') x++;
}
}
void PPP(int i,int j)
{
for(int dy=-1;dy<=1;dy++)
for(int dx=-1;dx<=1;dx++)
if(A[i+dy][j+dx]=='#') A[i+dy][j+dx]='P';
}
void KKK(int i,int j,int &ans)
{
for(int dy=-1;dy<=1;dy++)
for(int dx=-1;dx<=1;dx++)
if(A[i+dy][j+dx]=='#') {A[i+dy][j+dx]='K';ans++;}
}
int Handle(int L)
{
//for(int i=1;i<=L;i++) puts(A[i]+1);
int x,y,z;int ans=0;
for(int O=1;O<=7;O++)
{
for(int i=1;i<=L;i++)
for(int j=1;j<=L;j++)
{
if(A[i][j]<='9'||A[i][j]>='0')
{
z = A[i][j] - '0';
Judge(x,y,i,j);
if(x+y==z) PPP(i,j);
else if(y==z) KKK(i,j,ans);
}
}
}
return ans;
}
int main()
{
//freopen("D:\\test.txt","r",stdin);
int T;scanf(("%d\n"),&T);
for(int i=1,L;i<=T&&scanf("%d\n",&L);i++)
{
memset(A,0,sizeof(A));
for(int j=1;j<=L;j++) gets(A[j]+1);
printf("Case #%d: %d\n",i,Handle(L));
}
}