题目描述
输入输出格式
输入格式:
第一行为N,第二行有N个数,依次为第二列的格子中的数。(1<= N <= 10000)
输出格式:
一个数,即第一列中雷的摆放方案数。
输入输出样例
如果从上到下确定每个雷的位置,那么判断某个位置能否为雷只受它上面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;
}