[SCOI2005]互不侵犯

[SCOI2005]互不侵犯

题目描述

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

注:数据有加强(2018/4/25)

输入输出格式

输入格式:

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

输出格式:

所得的方案数

输入输出样例

输入样例#1:

3 2

输出样例#1:

16

\(n \le 9\)可以看粗应该是个状压DP

所以就可以高高兴兴个设状态了

\(f[i][j][k]\)表示前\(i\) 行 最后一行状态为\(j\)\(k\)个的方案数

需要两个函数判断状态合不合法还有放置的个数

inline int bitcnt(int x,int ans=0){
	for(;x;x&=(x-1),++ans);
	return ans;
}

用来判断二进制中有几个一(\(x\&=x-1\)是不是很熟悉没错就是今年初赛题)

inline bool pd(int x){
	return (((x<<1)&x)||((x>>1)&x));
}

这个用来判断一个数合不合法(一个1左右不能有另一个1)

然后就可以列状态转移方程了

\(\displaystyle f[i][j][k]=\sum_{l与j合法}f[i-1][l][k-bitcnt(k)]\)

#include<iostream>
#include<cstdio>
using namespace std;
#define int long long 
int mian(){
	int s=0,f=1;char ch;
	while(!isdigit(ch=getchar()))(ch=='-')&&(f=-1);
	for(s=ch-'0';isdigit(ch=getchar());s=s*10+ch-'0');
	return s*f;
}
/*
子供の时の梦は言えますか
	その梦すら 沟に 舍てたのは
*/
int f[10][(1<<10)+1][100];
int n,m;
inline int bitcnt(int x,int ans=0){
	for(;x;x&=(x-1),++ans);
	return ans;
}
inline bool pd(int x){
	return (((x<<1)&x)||((x>>1)&x));
}
signed main(){
	n=mian(),m=mian();
	const int U = (1<<n)-1;
	f[0][0][0]=1;
	for(int i=1;i<=n;++i){
		for(int k=0;k<=U;++k){
			if(pd(k))continue;
			for(int l=0;l<=U;++l){
				if(pd(l))continue;
				if(l&k)continue;
				if((l>>1)&k)continue;
				if((l<<1)&k)continue;
				// cout<<k<<' '<<l<<endl;
				int c=bitcnt(k);
				for(int o=c;o<=m;++o)
					f[i][k][o]+=f[i-1][l][o-c];
			}
		}
	}
	int ans=0;
	for(int i=0;i<=U;++i)ans+=f[n][i][m];
	cout<<ans<<endl;
	return 0;
}
posted @ 2018-10-15 19:34  kraylas  阅读(133)  评论(0编辑  收藏  举报