HDU 5965 扫雷(递推)

1 篇文章 0 订阅

扫雷

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


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

2016年中国大学生程序设计竞赛(合肥)-重现赛(感谢安徽大学)




题意:


有一个3行N列的矩阵,第一行和第三行可以放置地雷,中间一行是数字,表示相邻9个格的地雷数,现在已知中间一行的数字,求可以放置地雷的方案。


我在第一眼看到这道题的时候就觉得是道DP题,因为数据给的太大,枚举不可能实现。所以刚开始就寻找这个问题的状态,我个人习惯先找状态,之后分析决策和阶段。

经过分析之后,找到状态dp[i][j][k],其中表示i表示第i列,k表示在第i列时放置k个地雷,j表示在第i+1列时应该放置的地雷数,  所以dp[i][j][k]表示在第i列放置k个地雷并且i+1列应该放置j个地雷时的最大方案数。

当我分析出来这个状态,求状态转移方程时发现,顺推更加简单,所以我把状态改成了递推式:把第二行的数据存入a数组里。

例如:当求出dp[i-1][j][k]时可以推出dp[i][a[i]-j-k][j]( 0<=j,k<=2,2>=a[i]-j-k>=0 ),

当j==1时:dp[i][a[i]-j-k][j]=dp[i-1][j][k]*2;(因为当j==1时,可以选择仅在第一行或第三行放置一个地雷)

当j==0 || j==2时: dp[i][a[i]-j-k][j]=dp[i-1][j][k]*1;

(其实我们中间可以发现一个规律:当第一列的地雷数确定时,第二列随之确定,同样,第三列的地雷数等于a[i]减去前两列的地雷数...同理,每一列的状态都是确定的。所以可以更简单一点的枚举第一列的地雷数。这道题就GG了。不过我懒得写了)


#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>
#define LL long long
#define mem(p,k) memset(p,k,sizeof(p));
using namespace std;
LL dp[10010][3][3];
char a[10010];
int main()
{
    int t;
    cin>>t;
        while(t--){
            scanf("%s",a+1);
            mem(dp,0);
            int len=strlen(a+1);
            for(int i=1;i<=len;i++)a[i]-=48;
            if(len==1){
                if(a[1]==1)cout<<2<<endl;
                else if(a[1]==0||a[1]==2)cout<<1<<endl;
                else cout<<0<<endl;
                continue;
            }//初始化第一列状态
            if(a[1]==0){
                dp[1][0][0]=1;
            }
            else if(a[1]==1){
                dp[1][0][1]=2;
                dp[1][1][0]=1;
            }
            else if(a[1]==2){
                dp[1][0][2]=1;
                dp[1][1][1]=2;
                dp[1][2][0]=1;
            }
            else if(a[1]==3){
                dp[1][2][1]=2;
                dp[1][1][2]=1;
            }
            else if(a[1]==4){
                dp[1][2][2]=1;
            }//
            for(int i=2;i<=len;i++){//if(i==9)cout<<dp[9][1][0]<<endl;
                for(int j=0;j<=2;j++){
                    for(int k=0;k<=2;k++){
                        if(dp[i-1][j][k]&&a[i]>=j+k&&a[i]-j-k<=2){


                            if(j==1){
                                dp[i][a[i]-j-k][j]+=dp[i-1][j][k]*2;
                                dp[i][a[i]-j-k][j]%=100000007;
                            }
                            else{
                                dp[i][a[i]-j-k][j]+=dp[i-1][j][k];
                                dp[i][a[i]-j-k][j]%=100000007;
                            }
                        }
                    }
                }


            }

            LL sum=0;

                for(int j=0;j<=2;j++){
                    sum+=dp[len][0][j];
                    sum%=100000007;
                }

            cout<<sum<<endl;

        }


    return 0;
}




   

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值