Sample Input
3 3 2
Sample Output
24
分析
做状压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 ff)
{
if(dep>n)
{
js++;
a[js]=ans;
num[js]=ff;
return;
}
dfs(ans,dep+1,ff);
dfs(ans+(1<<(dep-1)),dep+2,ff+1);
}
然后进行DP。
设
f
[
i
]
[
a
[
j
]
]
[
l
]
f[i][a[j]][l]
f[i][a[j]][l]为第i行a[j]的状态下棋子为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之后累加所有方案数。
上代码
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
typedef long long ll;
using namespace std;
int n,m,k,js,ans;
int a[100001],num[100001],c[100001];
int f[82][1<<9][21];
void dfs(int ans,int dep,int ff)//dfs枚举每一行的状态
{
if(dep>n)//当前行结束,统计
{
js++;
a[js]=ans;
num[js]=ff;
return;
}
dfs(ans,dep+1,ff);//这个点不放
dfs(ans+(1<<(dep-1)),dep+2,ff+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)就不能转移,只有(0,0),(0,1),(1,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;
}