【BZOJ1087】【SCOI2005】互不侵犯King

Description

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

左下右上右下八个方向上附近的各一个格子,共8个格子。

Input

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

Output

方案数。

Sample Input

3 2

Sample Output

16

Solution

状压DP,\(f[i][j][S]\)表示前\(i\)行放\(j\)个棋子状态为\(S\)的方案数,枚举上一个转移的状态转移过来即可。

方程为$f[i][j][S]=\Sigma f[i-1][j-l][P] $(P为合法前驱状态,l为当前状态的1的个数).

时间复杂度\(O(nk2^{2n})\),记得跳过不合法与不必要的状态。

Code

#include <stdio.h>
#define MK 9
#define R register
#define ll long long
#define file(x) freopen(#x".in","r",stdin);freopen(#x".out","w",stdout);
#define end fclose(stdin);fclose(stdout)
inline int read(){
	R int x; R bool f; R char c;
	for (f=0; (c=getchar())<'0'||c>'9'; f=c=='-');
	for (x=c-'0'; (c=getchar())>='0'&&c<='9'; x=(x<<3)+(x<<1)+c-'0');
	return f?-x:x;
}
ll f[MK+5][MK*MK+5][1<<MK],ans;int n,k;
inline int getlen(int S){
	R int res=0;for (R int i=0; i<n; ++i)
		res+=(bool)((1<<i)&S);return res;
}
inline bool check(int x,int y){
	if (x&y) return 1;
	if ((x>>1)&y) return 1;
	if ((x<<1)&y) return 1;
	return 0;
}
int main(){
	n=read(),k=read();f[0][0][0]=1ll;
	for (R int i=1; i<=n; ++i)
		for (R int j=0; j<=k; ++j)
			for (R int S=0; S<(1<<n); ++S){
				R int l=getlen(S);
				if (l>j) continue;
				if (S&(S>>1)) continue;
				if (S&(S<<1)) continue;
				for (R int P=0; P<(1<<n); ++P){
					if (!f[i-1][j-l][P]) continue;
					if (check(S,P)) continue;
					if (P&(P>>1)) continue;
					if (P&(P<<1)) continue;
					f[i][j][S]+=f[i-1][j-l][P];
				}
			}	
	for (R int S=0; S<(1<<n); ++S) ans+=f[n][k][S];
	printf("%lld\n",ans);
	return 0;
}
posted @ 2017-10-26 14:48  Melacau  阅读(199)  评论(0编辑  收藏  举报