扫雷 洛谷p2327

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/MrTinTin/article/details/78439831

题目描述

输入输出格式

输入格式:

第一行为N,第二行有N个数,依次为第二列的格子中的数。(1<= N <= 10000)

输出格式:

一个数,即第一列中雷的摆放方案数。

输入输出样例

输入样例#1:  复制
2
1  1
输出样例#1:  复制
2

如果从上到下确定每个雷的位置,那么判断某个位置能否为雷只受它上面2格雷的情况影响,于是可以将i-2~i的雷记录为一个3位的二进制数。考虑状压dp,dp[i][sta]表示前i个雷,i-2~i的雷的分布为sta时的总数,那么dp[i][sta]=dp[i-1][(sta>>1)|4]+dp[i-1][sta>>1],表示第i-2的位置放不放雷。转移前需要判断sta这个状态是否合法。设counter(x)为x的二进制表示中1的个数,num为第二列对应的数字,合法也就意味着counter(sta)==num[i-1]且counter(sta>>1)<=num[i],此时就可以进行转移。

#include<iostream>
#include<cstdio>
#define f(i,l,r) for(i=(l);i<=(r);i++)
using namespace std;
const int MAXN=10005;
int n,a[MAXN];
int dp[MAXN][8],f[8];
inline int cal(int x)
{
	int i,num=0;
	for(i=x;i;i-=(i&(-i))){
		num++;
	}
	return num;
}
inline void MakeTable()
{
	int i;
	f(i,0,7) f[i]=cal(i);
}
int main()
{
//	ios::sync_with_stdio(false);
//	freopen("data.out","r",stdin);
//	freopen("test.out","w",stdout);
	int i,j,ans=0;
	MakeTable();
	cin>>n;
	f(i,1,n)  cin>>a[i];
	dp[1][1]=dp[1][0]=1;
	f(i,2,n-1){
		f(j,0,7){
			if(f[j]!=a[i-1]||f[j&3]>a[i]) continue;
			dp[i][j]=dp[i-1][(j>>1)|4]+dp[i-1][j>>1];
		//	cout<<i<<' '<<j<<' '<<dp[i-1][(j>>1)|4]<<' '<<dp[i-1][j>>1]<<"GG"<<endl;
		}
	}
	f(i,0,7){
		if(f[i]!=a[n-1]||f[i&3]!=a[n])  continue;
		ans+=dp[n-1][(i>>1)|4]+dp[n-1][i>>1];
	}
	cout<<ans<<endl;
	return 0;
}


猜你喜欢

转载自blog.csdn.net/MrTinTin/article/details/78439831