[SCOI2005]互不侵犯King

题意

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

\(N \leq 9,K \leq N \times N\)

分析

又是一道看题意和数据范围就能猜出状态\(f(i,k,s)\)的题。

表示前\(i\)行放\(k\)个国王,第\(i\)行的状态为\(s\)的方案数。

然后是一些小技巧,能攻击到就是左右移或不移,并起来为0。可以预处理合法状态,状态中的1的个数,合法可转移的状态。

时间复杂度\(O(N K 2^{2N})\)

代码

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cmath>
#include<set>
#include<map>
#include<queue>
#include<stack>
#include<algorithm>
#include<bitset>
#include<cassert>
#include<ctime>
#include<cstring>
#define rg register
#define il inline
#define co const
template<class T>il T read()
{
	rg T data=0;
	rg int w=1;
	rg char ch=getchar();
	while(!isdigit(ch))
	{
		if(ch=='-')
			w=-1;
		ch=getchar();
	}
	while(isdigit(ch))
	{
		data=data*10+ch-'0';
		ch=getchar();
	}
	return data*w;
}
template<class T>T read(T&x)
{
	return x=read<T>();
}
using namespace std;
typedef long long ll;

co int N=9;
int n,m;
int cnt[1<<N],c1[1<<N],c2[1<<N][1<<N];
ll f[N+1][N*N+1][1<<N];

void init()
{
	for(int i=0;i<(1<<n);++i)
		if((i & (i >> 1)) == 0)
		{
			int s=0;
			for(int x=i;x;x>>=1)
				s+=(x&1);
			cnt[i]=s;
			c1[i]=1;
		}
	for(int i=0;i<(1<<n);++i)
		if(c1[i])
			for(int j=0;j<(1<<n);++j)
				if(c1[j])
	if((i & j) == 0 && (i & (j >> 1)) == 0 && (j & (i >> 1)) == 0)
		c2[i][j]=1;
}

void solve()
{
	for(int i=0;i<(1<<n);++i)
		if(c1[i])
			f[1][cnt[i]][i]=1;
	for(int j=1;j<n;++j)
		for(int k=0;k<(1<<n);++k)
			if(c1[k])
				for(int i=0;i<(1<<n);++i)
					if(c1[i])
	if(c2[k][i])
		for(int p=cnt[k];p+cnt[i]<=m;++p)
			f[j+1][p+cnt[i]][i]+=f[j][p][k];
}

int main()
{
//	freopen(".in","r",stdin);
//	freopen(".out","w",stdout);
	read(n);read(m);
	init();
	solve();
	ll ans=0;
	for(int i=0;i<(1<<n);++i)
		ans+=f[n][m][i];
	printf("%lld\n",ans);
	return 0;
}

posted on 2018-12-04 10:13  autoint  阅读(126)  评论(0编辑  收藏  举报

导航