[BZOJ1087][SCOI2005]互不侵犯King(状压dp)

来源:互联网 发布:wordpress 速度优化 编辑:程序博客网 时间:2024/05/16 15:38

题目描述

传送门

题解

用二进制数表示一行放国王的状态,预处理某种状态是否能在一行出现,以及某两种状态是否可以放在相邻的两行。
状态:f[i][j][k]表示放到第i行一共放了j的国王状态为k的方案数。
转移:f[i][j][k]=f[i-1][j-cnt[i]][k’],其中cnt[i]表示这个状态一行中国王的数量,k’表示转移来的状态。

代码

#include<iostream>#include<cstring>#include<cstdio>using namespace std;#define LL long longconst int N=10;int n,m,tot,cnt[1<<N];bool line[1<<N],ok[1<<N][1<<N];LL f[N][N*N][1<<N],ans;int main(){    scanf("%d%d",&n,&m);    tot=(1<<n)-1;    for (int i=0;i<=tot;++i)      if (((i>>1)&i)==0){        for (int j=i;j>0;j>>=1) cnt[i]+=(j&1);        line[i]=true;      }    for (int i=0;i<=tot;++i) if (line[i])      for (int j=0;j<=tot;++j) if (line[j])        if ((i&j)==0&&((i>>1)&j)==0&&((j>>1)&i)==0)          ok[i][j]=true;    for (int i=0;i<=tot;++i) if (line[i]) f[1][cnt[i]][i]=1;    for (int p=2;p<=n;++p)      for (int i=0;i<=tot;++i) if (line[i])        for (int j=0;j<=tot;++j) if (line[j])          if (ok[i][j])            for (int k=cnt[i];k+cnt[j]<=m;++k)               f[p][k+cnt[j]][j]+=f[p-1][k][i];    for (int i=0;i<=tot;++i) ans+=f[n][m][i];    printf("%lld",ans);}
0 0