P1896 状压

Posted on 2020-10-14 21:56  Choimoe  阅读(6)  评论(0)    收藏  举报

P1896 [SCOI2005]互不侵犯

题目描述

在N×N的棋盘里面放K个国王,使他们互不攻击,共有多少种摆放方案。国王能攻击到它上下左右,以及左上左下右上右下八个方向上附近的各一个格子,共8个格子。

注:数据有加强(2018/4/25)

输入格式

只有一行,包含两个数N,K ( 1 <=N <=9, 0 <= K <= N * N)

输出格式

所得的方案数

题解

简单状压,令 \(f(i,j,\text S)\) 表示前 \(i\) 行选了 \(j\) 个,上一行的状态为 \(\text S\)(这位选了就是 \(1\) 否则就是 \(0\)),同时预处理 \(g(\text S)\) 表示二进制中 \(1\) 的个数(可以使用 __builtin_popcount),就不难得到

\[f(i,j,\text S)=\sum\limits_{\text T\in \text S}[\text {T is legal}]f(i-1,j-g(\text T),\text T) \]

注意到 \([\text {T is legal}]\) 可以预处理,于是就可以进一步优化了

这里本文中是通过预处理出每一行合法的状态

struct St{int s,p;}G[1<<N];
int n,k,f[N+1][K+1][1<<N|1],c,R;

void ini(){
	G[++c]=(St){0,0};
	for(int S=A;S;S=(S-1)&A){
		if((S<<1)&S)continue;
		G[++c]=(St){S,__builtin_popcountll(S)};
	}
	jk(i,1,c)if(G[i].p<=k)f[1][G[i].p][i]=1;
}

signed main(){
	n=rd();k=rd();
	ini();
	jk(i,2,n)jk(p,1,c)jk(w,1,c){
		if(G[p].s&G[w].s||G[p].s&(G[w].s<<1)||(G[p].s<<1)&G[w].s)continue;
		jk(s,1,k)if(G[p].p+s<=k)f[i][G[p].p+s][p]+=f[i-1][s][w];
	}
	jk(i,1,n)jk(j,1,c)R+=f[i][k][j];
	P(R);
}