扫雷

3 篇文章 0 订阅

扫雷

                                                                  Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Others)
                                                                                     Total Submission(s): 1502    Accepted Submission(s): 413


Problem Description
扫雷游戏是晨晨和小璐特别喜欢的智力游戏,她俩最近沉迷其中无法自拔。
该游戏的界面是一个矩阵,矩阵中有些格子中有一个地雷,其余格子中没有地雷。 游戏中,格子可能处于己知和未知的状态。如果一个己知的格子中没有地雷,那么该 格子上会写有一个一位数,表示与这个格子八连通相邻的格子中地雷总的数量。
现在,晨晨和小璐在一个3行N列(均从1开始用连续正整数编号)的矩阵中进 行游戏,在这个矩阵中,第2行的格子全部是己知的,并且其中均没有地雷;而另外 两行中是未知的,并且其中的地雷总数量也是未知的。
晨晨和小璐想知道,第1行和第3行有多少种合法的埋放地雷的方案。
 

Input
包含多组测试数据,第一行一个正整数T,表示数据组数。
每组数据由一行仅由数字组成的长度为N的非空字符串组成,表示矩阵有3行N 列,字符串的第i个数字字符表示矩阵中第2行第i个格子中的数字。
保证字符串长度N <= 10000,数据组数<= 100。
 

Output
每行仅一个数字,表示安放地雷的方案数mod100,000,007的结果。
 

Sample Input
  
  
2 22 000
 

Sample Output
  
  
6 1
 

Source

分析:
  拿到这道题,首先想到的是深搜加剪枝,对于第一行和第二行的每个方格,有和没有地雷分别进行考虑,这种情况下稍微可以优化的就是,每找到一个八方格,就按照中间一行的数字进行判断是否符合扫雷规则,是就继续往下搜索,不是那么这种情况就不符合,此时结束这种情况的深搜,继续其他路径的深搜。
  但是和队友讨论之后,感觉这种方法并不会通过,于是就考虑另外的方法。
  我想到的另一个方法,就是将第一行和第三行的地雷合并,那么此时就只有两行,第一行就是原先中间的一行表示数字,第二行就是存放原先同列的第一行和第三行的地雷树,那么在第一列,我们确定了一其地雷数后,可以根据放地雷规则,依次找到后面每列的地雷数,具体如下图:
           
其中蓝色区域就是体现的地雷图的规则。根据规则,在n1下的地雷数确定后,可以依次往后推导出最后一行每个方格的地雷数,一直可以得到最后一列的地雷数,但是这里n[len-1]下的地雷数是利用n[len-2]根据规则推导出的,最后我们就根据规则,判断n[len-1]周围的八方格是否满足要求,也就是" n[len-2]下的地雷数+n[len-1]下的地雷数==n[len-1]",如果该成立,说明一开始我们令n1下的地雷数为x是满足要求的。
  这时求方案数,首先我们要知道,最后一行的每个方格的地雷数只能是0,1,2中的一个,因为其是由原先两个方格组合成,所以地雷数是原先每个方格数的两倍范围,即[0,1]*2=[0,2]。
  求方案数的时候,因为每个方格的地雷数都是各自独立的,如果为0,那么返回原先的情况,必须第一行和第二行的同列方格中每个方格地雷数都是0,就有一种情况;如果是1,那么同理就有两种情况,第一行有地雷,第三行没有或者反过来; 如果是2,那么同理原先的对应两个方格必须都有地雷,只有一种情况。最后我们可以很容易使用类似独立事件的方法,将每个方格数中的地雷数根据刚才介绍的规则转换为方法数。
  最后,由于合并后的每个方格地雷数为0,1,2中的一种,我们将x=0,1,2的情况总数加起来即可。以下是源码:
#include<iostream>
#include<string.h>
#include<cstdio>
using namespace std;

#define Max 10000
int zhuan[]={1,2,1};
int arr[Max];
int main()
{

   int N;
   cin>>N;
while(N--)
{
  char str[10005];
  scanf("%s",str);
  int len=strlen(str);
  int sum=0;
  for(int x=0;x<=2;x++)
  {
       arr[0]=x;
       arr[1]=str[0]-'0'-x;

       if(arr[1]<0 || arr[1]>2)
       {
         continue;
       }

       bool isContinue=false;
       for(int i=2;i<=len-1;i++)
       {
           arr[i]=str[i-1]-'0'-arr[i-1]-arr[i-2];
           if(arr[i]<0 || arr[i]>2)
           {
              isContinue=true;
              break;
           }
       }
       if(isContinue)
       {continue;}
       int last=str[len-1]-'0';
       if(arr[len-2]+arr[len-1]==last)
       {
            int cheng=1;
            for(int j=0;j<=len-1;j++)
            {
                cheng*=zhuan[arr[j]];
                cheng%=100000007;
            }
            sum+=cheng;
            sum%=100000007;
       }
  }
  printf("%d\n",sum);
  }
  return 0;
}
最后需要提醒的是,这里输入输出,最好使用C语言的printf和scanf,并且输入数据利用字符数组保存,如果直接使用cin,cout和字符串类型接收输入数据,那么就会超时.(亲测提醒).



PS:好久没写博客了,比完蓝桥杯,也放松了。好消息是,哥哥要去北京了,哈哈。最近在老师的指导下,准备和学院的大牛组队参加CCPC,这道题目就是今早在和同学模拟刷题时遇到的。CCPC,蓝桥杯决赛,加油吧~
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值