HDOJ 5965 扫雷 【DP】

扫雷

TimeLimit: 2000/1000 MS (Java/Others)    Memory Limit:65536/65536 K (Java/Others)
Total Submission(s): 1806    Accepted Submission(s): 491

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



题意:略

思路:用dp[i][j][k]表示考虑到第i列,且第i列和第i-1列一共有j个地雷,第i行有k个地雷的方案数。用num[i]来表示某一行放置i个地雷有多少情况。

那么可以写出状态转移方程:

dp[i][j][k]=dp[i-1][pre_j][pre_k]*ans[k](pre_j,pre_k为前一列的状态量)

分析方程:

  1. 第i列有k个地雷。
  2. 第i-1列有j-k(pre_k)个地雷。
  3. 第i-2列有pre_j-pre_k个地雷。

可以得到关系式:

  1. pre_k=j-k.
  2. pre_j+k=a[i-1] (第i-1列周围的地雷数)

然后三重循环状态转移,且将最后一列所有可能的情况累加即可。(注意特殊情况的特判)

PS:此题这种做法有点卡时限,需要多提交几次。

可能的优化方法TUT。。。

把数组开小,开到刚刚好,如果dp[][][]的后两维都开到10,就一定TLE。


#include <cstdio>
#include <iostream>
#include <vector>
#include <string>
#include <cstring>
#include <algorithm>
using namespace std;
#define mst(a,b) memset((a),(b),sizeof(a))
#define f(i,a,b) for(int i=(a);i<=(b);++i)
#define rush() int T;scanf("%d",&T);while(T--)

typedef long long ll;
const int maxn= 10005;
const int mod = 1e8+7;
const int INF = 0x3f3f3f3f;
const double eps = 1e-6;
//dp[i][j][k]表示到第i列,这一列的和前一列的地雷数为j个,并且这一列的地雷数为k个的方案数

int a[maxn];
char s[maxn];
ll dp[maxn][5][3];
int num[3]={1,2,1};

int main()
{
    rush()
    {
        scanf("%s",s);
        int len=strlen(s);
        int flag=0;
        for(int i=1;i<=len;i++)
        {
            a[i]=s[i-1]-'0';
            if((i==1||i==len)&&a[i]>4) flag=1;
            if(a[i]>6) flag=1;
        }
        if(flag)
        {
            puts("0");
            continue;
        }
        if(len==1)
        {
            if(a[1]<=2) printf("%d\n",num[a[1]]);
            else puts("0");
            continue;
        }
        mst(dp,0);
        for(int i=0;i<=2;i++)
        {
            if(a[1]>=i)
                dp[1][i][i]=num[i];
            else dp[1][i][i]=0;
        }
        for(int i=2;i<=len;i++)
        for(int j=0;j<=4;j++)
        for(int k=0;k<=2&&k<=j;k++)
        {
            int pre_j=a[i-1]-k;
            int pre_k=j-k;
            if(pre_j>=0&&pre_k>=0&&pre_j>=pre_k)
            {
                dp[i][j][k]=num[k]*dp[i-1][pre_j][pre_k];
                dp[i][j][k]%=mod;
            }
        }
        ll ans=0;
        for(int i=0;i<=a[len]&&i<=2;i++)
        {
            ans+=dp[len][a[len]][i];
            ans%=mod;
        }
        printf("%I64d\n",ans);
    }
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值