[bzoj1087] [SCOI2005]互不侵犯King
Posted FallDream
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了[bzoj1087] [SCOI2005]互不侵犯King相关的知识,希望对你有一定的参考价值。
http://www.lydsy.com/JudgeOnline/problem.php?id=1087
一个n*n的棋盘,要在上面放国王,每个国王占领周围3*3的土地,求放置K个国王的方案数量。
一开始感觉结论题就打了个表,打一个8的表只要一两分钟⬇️,虽然说n<=9,但是9的估计要很久...说不定考试那几个小时都打不完。
说说正解吧。
考虑壮压dp,如果按照一个个位置dp需要记录轮廓线以及左上角那个位置的情况,但是显然我们不需要那么复杂。
情况数2^9=512 预先处理好 每种情况是否合法 需要多少国王 两种情况之间是否可以转移 ,就可以啦。
f[i][j][k]表示前i行用了j个国王,第i行状态是k的个数。复杂度n*K*2^(2*n)
#include<iostream> #include<cstdio> #define ll long long using namespace std; inline int read() { int x=0,f=1;char ch=getchar(); while(ch<\'0\'||ch>\'9\'){if(ch==\'-\') f=-1;ch=getchar();} while(ch>=\'0\'&&ch<=\'9\'){x=x*10+ch-\'0\'; ch=getchar();} return x*f; } bool b[515][515]; int n,K; ll f[10][105][515]; int use[515]; int main() { n=read();K=read();int to=(1<<n); for(int i=0;i<to;i++) { int j=0,k=i,tot=0; while(k) { if((k&1)&&j) {use[i]=-1;break;} j=(k&1);tot+=j;k>>=1; } if(use[i]!=-1) use[i]=tot; } for(int i=0;i<to;i++) for(int j=0;j<to;j++) if(use[i]!=-1&&use[j]!=-1) { b[i][j]=1; for(int k=1;k<to;k<<=1) if((i&k)&&((k!=1&&(j&(k>>1)))||(j&(k<<1))||(j&k))) {b[i][j]=0;break;} } f[0][0][0]=1; for(int i=1;i<=n;i++) for(int j=0;j<=K;j++) for(int k=0;k<to;k++) if(use[k]!=-1) for(int l=0;l<to;l++) if(use[l]!=-1&&b[k][l]&&use[l]<=j) f[i][j][l]+=f[i-1][j-use[l]][k]; ll ans=0; for(int i=0;i<to;i++) ans+=f[n][K][i]; cout<<ans; return 0; }
以上是关于[bzoj1087] [SCOI2005]互不侵犯King的主要内容,如果未能解决你的问题,请参考以下文章
BZOJ 1087: [SCOI2005]互不侵犯King [状压DP]