BZOJ 1087 互不侵犯

时间:2023-12-25 14:09:19

Description

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

Input

只有一行,包含两个数$N,K $。

Output

方案数。

Sample Input

3 2

Sample Output

16

HINT

$ 1 le N \le 9, 0 \le K \le N \times N$

一道很明显的状态压缩dp题。

题解就不谢了,直接上代码(代码复杂度有点虚,还是能过。有许多的地方可以优化)吧。

可以使用轮廓线(插头)dp来优化复杂度(lyp教我的)。

#include<cstdio>
#include<cstdlib>
using namespace std; typedef long long ll;
#define maxn (11)
int N,K; ll f[82][maxn][1<<maxn],g[82][maxn][1<<maxn]; inline int oc(int x)
{
x=(x&0x55555555UL)+((x>>1)&0x55555555UL); //1
x=(x&0x33333333UL)+((x>>2)&0x33333333UL); //2
x=(x&0x0f0f0f0fUL)+((x>>4)&0x0f0f0f0fUL); //3
x=(x&0x00ff00ffUL)+((x>>8)&0x00ff00ffUL); //4
x=(x&0x0000ffffUL)+((x>>16)&0x0000ffffUL);//5
return x;
} inline bool okay(int x) { return !(x&(x>>1)); } inline bool fit(int x,int y) { return !(x&y); } int main()
{
freopen("1087.in","r",stdin);
freopen("1087.out","w",stdout);
scanf("%d %d",&N,&K);
g[0][1][0] = 1;
for (int i = 1;i <= N;++i)
{
for (int l = 0;l <= K;++l)
for (int j = 0;j < (1<<N);++j)
if (g[i][j])
for (int k = 0;k < (1<<N);++k)
if (l+oc(k) <= K && okay(k) && fit(j,k))
f[l+oc(k)][i][k] += g[l][i][j];
for (int l = 0;l <= K;++l)
for (int j = 0;j < (1<<N);++j)
{
if (!f[l][i][j]) continue;
int p = 0;
for (int k = 0;k < N;++k)
if (j & (1<<k))
{
p |= 1<<k;
if (k) p|=1<<(k-1);
if (k < N-1) p |= 1<<(k+1);
}
g[l][i+1][p] += f[l][i][j];
}
}
ll ans = 0;
for (int i = 0;i < (1<<N);++i) ans += f[K][N][i];
printf("%lld",ans);
fclose(stdin); fclose(stdout);
return 0;
}