这题也是一道 状 压 D P 状压DP 状压DP 题
首先,我们要用DFS枚举每一行的状态。
用 a [ j s ] a[js] a[js] 记录状压后的二进制数, n u m [ j s ] num[js] num[js] 记录每行棋子的个数.
void dfs(int ans,int dep,int flag)
{
if(dep>n) //当前行结束,统计
{
a[++js]=ans;
num[js]=flag;
return;
}
dfs(ans,dep+1,flag); //不放棋子
dfs(ans+(1<<(dep-1)),dep+2,flag+1); //放棋子
}
然后我们开始DP。
设 f [ i ] [ a [ j ] ] [ l ] f[i][a[j]][l] f[i][a[j]][l] 表示在第 i i i 行状态为 s [ j ] s[j] s[j] 的情况下有 l l l 枚棋子所得的方案数
可得动态转移方程:
f [ i ] [ a [ j ] ] [ l ] + = f [ i − 1 ] [ a [ w ] ] [ l − n u m [ j ] ] f[i][a[j]][l]+=f[i-1][a[w]][l-num[j]] f[i][a[j]][l]+=f[i−1][a[w]][l−num[j]];
DP前要初始化第1行的所有方案。
DP后要累加总方案数。
代码
#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
#include<cmath>
using namespace std;
int a[100010],num[100010],f[82][1<<9][21],c[100010];
int n,m,k,js,ans;
void dfs(int ans,int dep,int flag)
{
if(dep>n) //当前行结束,统计
{
a[++js]=ans;
num[js]=flag;
return;
}
dfs(ans,dep+1,flag); //不放棋子
dfs(ans+(1<<(dep-1)),dep+2,flag+1); //放棋子
}
int main()
{
cin>>n>>m>>k;
if(n<m)
swap(n,m);
dfs(0,1,0); //从第一个开始
for(int i=1; i<=js; i++) //初始化
f[1][a[i]][num[i]]=1;
for(int i=2; i<=m; i++)
for(int j=1; j<=js; j++)
for(int w=1; w<=js; w++)
{
if(a[j]&a[w]) //出现1,1的情况就不能转移(只有1,0;0,1;0,0可以转移)
continue;
for(int l=0; l<=k; l++)
if(l>=num[j]) //防止减出负数
f[i][a[j]][l]+=f[i-1][a[w]][l-num[j]];
}
for(int i=1; i<=js; i++)
ans+=f[m][a[i]][k]; //累加方案数
cout<<ans;
return 0;
}