【BZOJ】1087: [SCOI2005]互不侵犯King

时间:2023-03-09 09:14:05
【BZOJ】1087: [SCOI2005]互不侵犯King

【算法】状态压缩型DP

【题解】http://www.cnblogs.com/xtx1999/p/4620227.html (orz)

https://www.cnblogs.com/zbtrs/p/6189240.html

dp[i][j][k]为前i行已经放了j个国王并且第i行的状态为k(二进制)的方案数。

状态左移右移预处理两行合法。

dp[i][j][k] = Σdp[i-1][j - num[k]][p]

#include<cstdio>
#include<algorithm>
using namespace std;
const int maxn=,e2=;
long long f[maxn][e2][maxn*maxn];
int n,mk,num[e2],king[e2],ok[e2][e2];
bool selfcheck(int a)
{
return !(a&(a>>));
}
int calc(int a)
{
int cyc=;
while(a)
{
if(a&)cyc++;
a=a>>;
}
return cyc;
}
bool check(int a,int b)
{
if((a&b)||(a&(b<<))||(a&(b>>)))return ;
return ; }
int main()
{
scanf("%d%d",&n,&mk);
int all=(<<n)-,tot=;
for(int i=;i<=all;i++)
{
if(selfcheck(i))
{
num[++tot]=i;
king[tot]=calc(i);
//printf("%d\n",i);
}
}
for(int i=;i<=tot;i++)
for(int j=;j<=tot;j++)
if(!ok[i][j])if(check(num[i],num[j]))
ok[i][j]=ok[j][i]=;//,printf("%d %d\n",i,j);
f[][][]=;
for(int i=;i<=n-;i++)
for(int j=;j<=tot;j++)
for(int k=;k<=mk;k++)
if(f[i][j][k])
for(int p=;p<=tot;p++)
if(ok[j][p]&&k+king[p]<=mk)
f[i+][p][k+king[p]]+=f[i][j][k];
long long ans=;
for(int i=;i<=tot;i++)
ans+=f[n][i][mk];
printf("%lld",ans);
return ;
}