问题 H: 扫雷

问题 H: 扫雷
时间限制: 1 Sec  内存限制: 128 MB
提交: 93  解决: 33
[提交][状态][讨论版]
题目描述

     扫雷是一款系统自带的十分经典的益智游戏,以上手简单而玩法灵活而深受人们喜爱。游戏的目标是在最短的时间内找出所有的安全格子并且避免踩雷。

游戏界面如下:

这里写图片描述

游戏规则如下:

所有格子分为已知格子和未知格子两种。其中未知格子,即未确定的格子,未点开且不能判断是否为地雷或安全格子的格子;已知格子,即已确定的格子,为数字、空白、地雷、安全格子4者中的一种------数字代表此格子周围的8个格子(有时不足8个,譬如边格子5个、角格子3个)里地雷的数目,用以辅助判断周围的格子是否安全;空白代表此格子周围没有地雷;安全格子是指确定不是地雷的未点开的格子。例如代表周围8个格子里有2个地雷(插旗子处),1个安全格子(标绿点处)如图。

2 这里写图片描述

       这里定义一个概念——局面,是指游戏进行到某个阶段时地图的状态,一旦点开某个未点开的格子(任何作标记的操作都不算),即进入下一个局面。
最近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));
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值