题目链接:
http://acm.hdu.edu.cn/showproblem.php?pid=4529
题目意思:
有一8*8的矩阵,放了8个皇后,让你放n个骑士,使骑士之间不能相互攻击(走日字)。
解题思路:
状态压缩 dp.
dp[i][j][p][q]:表示第i行状态为q(q写成二进制时,1表示放骑士,0表示不放骑士),第i-1行状态为p,并且前i行总的骑士的个数为j时总的方案总数。
对每一行,枚举每一种状态,依据前两行的状态,进行转移。
代码:
#include<iostream>
#include<cmath>
#include<cstdio>
#include<cstdlib>
#include<string>
#include<cstring>
#include<algorithm>
#include<vector>
#include<map>
#include<stack>
#include<list>
#include<queue>
#define eps 1e-6
#define INF (1<<30)
#define PI acos(-1.0)
using namespace std;
#define ll __int64
#define Maxn 60
ll dp[2][11][1<<8][1<<8]; //dp[i][j][p][q] 表示第i行放马的状态为q,
//第i-1行放马的状态为p,前i行放马的总个数为j个的总方案数
bool flag1[1<<8][1<<8],flag2[1<<8][1<<8];
bool iscan(int a,int b,int d) //判断状态a和状态b之差是不是相隔d列的
{
for(int i=0;i+d<8;i++)
{
int x=1<<i;
int y=1<<(i+d);
if((a&x)&&(b&y))
return false;
if((b&x)&&(a&y))
return false;
}
return true;
}
int num[11];//统计每一行中皇后所在位置的状态
int cnt[1<<8]; //每种状态中1的个数
char save[11];
int cal(int x) //统计该状态下含1的个数
{
int res=0;
while(x)
{
res+=(x&1);
x=x>>1;
}
return res;
}
int main()
{
for(int i=0;i<(1<<8);i++)
{
for(int j=0;j<(1<<8);j++)
{
flag1[i][j]=iscan(i,j,1); //预处理下相隔为1的情况
flag2[i][j]=iscan(i,j,2); //预处理下相隔为2的情况
}
cnt[i]=cal(i);
}
int t,n;
scanf("%d",&t);
while(t--)
{
scanf("%d",&n);
for(int i=0;i<8;i++)
{
scanf("%s",save);
num[i]=0;
for(int j=0;j<8;j++) //计算皇后的位置状态
{
num[i]<<=1;
if(save[j]=='*')
num[i]++;
}
}
memset(dp,0,sizeof(dp));
//先预处理第一行的情况
for(int i=0;i<(1<<8);i++)
{
if(cnt[i]>n||(num[0]&i)) //有皇后的位置不能放
continue;
dp[0][cnt[i]][0][i]++;
}
int cur;
for(int i=1;i<8;i++) //滚动数组,注意每一步都要清零
{
cur=i&1;
for(int j=0;j<(1<<8);j++)
for(int k=0;k<(1<<8);k++)
for(int nn=0;nn<=n;nn++)
dp[cur][nn][j][k]=0;
for(int j=0;j<(1<<8);j++)
{
if((i-2)>=0&&(num[i-2]&j)) //不能放
continue;
for(int k=0;k<(1<<8);k++)
{
if(num[i-1]&k) //不能放
continue;
if(cnt[j]+cnt[k]>n)
continue;
if(flag2[j][k]==false)
continue;
int tt=cnt[j]+cnt[k];
for(int p=tt;p<=n;p++) //j表示i-2行的状态,k表示i-1行的状态
{
if(dp[cur^1][p][j][k]==0) //前面两行就不能满足了
continue;
for(int z=0;z<(1<<8);z++) //枚举当前行的状态,建立转移方程
{
if(!flag2[k][z]||!flag1[j][z])
continue;
if(z&num[i])
continue;
if(p+cnt[z]>n)
continue;
dp[cur][p+cnt[z]][k][z]+=dp[cur^1][p][j][k];
}
}
}
}
}
ll ans=0; //统计出答案
for(int i=0;i<(1<<8);i++)
for(int j=0;j<(1<<8);j++)
ans+=dp[cur][n][i][j];
printf("%I64d\n",ans);
}
return 0;
}