互不侵犯 (状压)
[SCOI2005] 互不侵犯
题目描述
在 \(N \times N\) 的棋盘里面放 \(K\) 个国王,使他们互不攻击,共有多少种摆放方案。国王能攻击到它上下左右,以及左上左下右上右下八个方向上附近的各一个格子,共 \(8\) 个格子。
输入格式
只有一行,包含两个数 \(N,K\)。
输出格式
所得的方案数
样例 #1
样例输入 #1
3 2
样例输出 #1
16
提示
数据范围及约定
对于全部数据,\(1 \le N \le 9\),\(0 \le K \le N\times N\)。
解析
状压板子题,把每一行压成一个二进制的状态,首先预处理行内所有可能的状态,
在不同行遍历的时候只考虑上一行的就可以了,
$ f[i][j][k] $ 表示在第 \(i\) 行,状态为 \(j\) ,已经放 \(k\) 个的方案数。
状压要敢于写循环吧,一眼数据范围,几层都炸不了。
#include<bits/stdc++.h>
using namespace std;
int n,k;
long long f[10][1<<10][100];
long long cnt[1<<10];
long long c[1<<10],num;
int main()
{
scanf("%d%d",&n,&k);
f[0][0][0]=1;
for(int i=0;i<(1 << n);i++)
{
long long tot=0,j=i;
while(j)
{
if(j & 1) tot++;
j >>= 1;
}
cnt[i]=tot;
if(!(((i<<1)|(i>>1)) & i)) c[++num]=i;
}
long long ans=0;
for(int i=1;i<=n;i++)
{
for(int j=1;j<=num;j++)
{
long long s=c[j];
for(int h=1;h<=num;h++)
{
long long s1=c[h];
if(!((s1 | (s1<<1) | (s1>>1)) & s))
for(int l=cnt[s];l<=k;l++)
f[i][s][l]+=f[i-1][s1][l-cnt[s]];
}
}
}
for(int i=1;i<=num;i++) ans+=f[n][c[i]][k];
printf("%lld",ans);
return 0;
}