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);
}