BZOJ 1087 互不侵犯

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;
}
posted @ 2015-03-05 10:53  lmxyy  阅读(132)  评论(0编辑  收藏  举报