车II
Time Limit:1000MS Memory Limit:65536K
Total Submit:103 Accepted:62
Description
有一个nm的棋盘(n、m≤80,nm≤80)要在棋盘上放k(k≤20)个棋子,使得任意两个棋子不相邻。求合法的方案总数。
Input
n,m,k
Output
方案总数
Sample Input
3 3 2
Sample Output
24
思路
车的拓展,
先dfs预处理出一行的全部合法(左右不相邻)状态且存储在s数组中。
f
i
,
j
,
k
f_{i,j,k}
fi,j,k表示第i行的状态为
s
j
s_j
sj且前i行总共放置k个棋子,
得:
f
0
,
1
,
0
=
1
f_{0,1,0}=1
f0,1,0=1
f
i
,
j
,
k
=
∑
f
i
−
1
,
l
,
k
−
c
[
j
]
f_{i,j,k}=\sum{f_{i-1,l,k-c[j]}}
fi,j,k=∑fi−1,l,k−c[j]
枚举行数i,状态j,当前总旗子k,上一行状态l,四重循环。
其中c[j]表示状态
s
j
s_j
sj中1的个数。且
s
j
&
s
l
=
=
0
s_j\&s_l==0
sj&sl==0,意思是
s
j
s_j
sj和
s
l
s_l
sl没有同为1的位,及满足上下不相邻。
最后发现f的第一维度行数i每次只会用到上一行,所以可以滚动,减少空间。
#include<iostream>
#include<cstring>
using namespace std;
int f[2][145][21];//滚动 DP数组
int n,m,kn,sn,s[145],c[145],ans;
void dfs(int dep,int ss,int sc)
{
if(dep>m)
{
s[++sn]=ss;
c[sn]=sc;
}
else
{
dfs(dep+1,ss,sc);
dfs(dep+2,ss+(1<<dep-1),sc+1);
}
}
int main()
{
cin>>n>>m>>kn;
if(m>n) swap(n,m);
dfs(1,0,0);
f[0][1][0]=1;
for(int i=1; i<=n; i++)//枚举行
{
for(int j=1; j<=sn; j++)//枚举当前状态
{
for(int k=c[j]; k<=kn; k++)//枚举前i行棋子个数
{
for(int l=1; l<=sn; l++)//枚举上一行状态
{
if(!(s[j]&s[l])&&k-c[j]>=c[l]) f[i&1][j][k]+=f[!(i&1)][l][k-c[j]];
}
}
}
memset(f[!(i&1)],0,sizeof(f[!(i&1)]));//清空用过的滚动数组
}
for(int j=1;j<=sn;j++) ans+=f[n&1][j][kn];
cout<<ans;
}