HDU-5965 扫雷(dp / 递推)

传送门

扫雷游戏是晨晨和小璐特别喜欢的智力游戏,她俩最近沉迷其中无法自拔。
该游戏的界面是一个矩阵,矩阵中有些格子中有一个地雷,其余格子中没有地雷。 游戏中,格子可能处于己知和未知的状态。如果一个己知的格子中没有地雷,那么该 格子上会写有一个一位数,表示与这个格子八连通相邻的格子中地雷总的数量。
现在,晨晨和小璐在一个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,然后枚举了三列的状态,一直超时,,没想到怎么优化,后来看了别人博客发现,只需要枚举两列就行了,第三列可以根据已知的这两列算出来。。dp[i][j][k]表示第i列地雷数为j,第 i-1 列地雷数为k的情况下的方案数有多少种。

或者直接递推,第一列的状态确定了,后面的就都确定了。每一列地雷数为1的话有两种放法,0或者2只有1种方法,所以只需要枚举第一列的地雷数,然后依次计算即可,时间复杂度为O(n)

dp做法:

#include<iostream>
#include<cstring>
#include<cstdio>
using namespace std;
const int N = 1e4+10,mod = 1e8+7;
int dp[N][10][10];
int main()
{
	ios::sync_with_stdio(false);
	cin.tie(0);
	int t,n;
	cin>>t;
	while(t--)
	{
		string s;
		cin>>s;
		n=s.size();s=" "+s;
		for(int i=0;i<=n;i++)
			for(int j=0;j<3;j++)
				for(int k=0;k<3;k++)
					dp[i][j][k]=0;
		for(int i=0;i<3;i++)
		{
			if(i>s[1]-'0') continue;
			if(i==1) dp[1][i][0]=2;
			else dp[1][i][0]=1;
		}
		for(int i=2;i<=n;i++)
		{
			for(int j=0;j<3;j++)
				for(int k=0;k<3;k++)
				{
					if(s[i-1]-'0'<j+k || s[i]-'0'<j+k) continue;
					int x=s[i-1]-'0'-j-k;
					dp[i][j][k]=(dp[i][j][k]+dp[i-1][k][x])%mod;
					if(j==1) dp[i][j][k]=(dp[i][j][k]+dp[i-1][k][x])%mod;
				}
		}
		int ans=0;
		for(int i=0;i<3;i++)
			for(int j=0;j<3;j++)
				if(i+j==(s[n]-'0'))  //只计算合法的方案数
					ans=(ans+dp[n][i][j])%mod;
		cout<<ans<<endl;
	}
	return 0;
 } 


递推做法:

#include<iostream>
#include<cstring>
#include<cstdio>
using namespace std;
const int N = 2e5+10,mod=1e8+7;
int num[N];
int main()
{
	ios::sync_with_stdio(false);
	cin.tie(0);
	int t;
	cin>>t;	
	while(t--)
	{
		string s;
		cin>>s;
		int n=s.size();
		int ans=0;
		s=" "+s;
		for(int i=0,j;i<3;i++)
		{
			if(i>s[1]-'0') continue;
			num[1]=i;
			int sum=(i==1)?2:1;
			for(j=2;j<=n;j++)
			{
				int x=(s[j-1]-'0')-num[j-1]-num[j-2];
				if(x==1) sum=sum*2%mod;
				else if(x<0||x>2) break;
				num[j]=x;
			}
			if(j<=n||(num[n-1]+num[n]!=s[n]-'0')) continue;
			ans=(ans+sum)%mod;
		}
		cout<<ans<<endl;
	}
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值