[关键字]:动态规划
[题目大意]:在一个n*n的棋盘里放k个王,问使它们不能互相攻击的摆放方案有多少种。
//==============================================================
[分析]:状态压缩动态规划,类似于炮兵阵地。方程:f[i][j][k1]=Σf[i-1][j-b[k1]][k2],表示前i行放j个王第i行状态是k1,i-1行状态时k2时的方案数。k1是利用二进制表示的这一行的状态,1表示有王0表示没有王。转移时要判断:(k1&k2)==0 ((k1<<1)&k2)==0 ((k1>>1)&k2)==0。开始时预处理出每一行所有的可行状态。
[代码]:
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
using namespace std;
int n,m,tot,sum;
int a[500],b[500];
long long f[12][102][500];
char s[20];
bool flag;
void Init()
{
scanf("%d%d",&n,&m);
for (int i=0;i<=(1<<n)-1;++i)
if ((i&(i<<1))==0 && ((i&(i>>1))==0))
{
a[++sum]=i;
int temp=i;
while (temp)
{
if (temp&1) ++b[sum];
temp>>=1;
}
}
//for (int i=1;i<=sum;++i) printf("%d %d\n",a[i],b[i]);
}
void Solve()
{
memset(f,0,sizeof(f));
for (int i=1;i<=sum;++i)
f[1][b[i]][i]=1;
for (int i=2;i<=n;++i)
for (int j=0;j<=m;++j)
if (i*n>=j)
for (int k1=1;k1<=sum;++k1)
if (b[k1]<=j)
for (int k2=1;k2<=sum;++k2)
if (b[k2]<=j && (a[k1]&a[k2])==0 && ((a[k1]<<1)&a[k2])==0 && ((a[k1]>>1)&a[k2])==0)
f[i][j][k1]+=f[i-1][j-b[k1]][k2];
long long ans=0;
for (int i=1;i<=sum;++i) ans+=f[n][m][i];
//if (m==0) ans=0;
printf("%I64d\n",ans);
}
int main()
{
freopen("in.txt","r",stdin);
freopen("out.txt","w",stdout);
Init();
Solve();
return 0;
}