首页 > 代码库 > 【BZOJ 1087】 [SCOI2005]互不侵犯King

【BZOJ 1087】 [SCOI2005]互不侵犯King

1087: [SCOI2005]互不侵犯King

Time Limit: 10 Sec  Memory Limit: 162 MB
Submit: 1664  Solved: 984
[Submit][Status]

Description

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

Input

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

Output

方案数。

Sample Input

3 2

Sample Output

16

简单的状压dp。


我首先预处理出一行中可行的方案(即满足没有左右相邻的国王),存在a数组中。


然后枚举a数组中所有两两组合的情况,判断是否可以成为相邻的两行(即满足上下不攻击的性质),用前向星存起来。


接下来就是状压dp了。


f[i][j][k]表示前i行用j个国王,第i行的国王放置情况为a[j]的方案数。


把i-1行与a[k]不冲突的方案数累加过来即可。


注意!!!这道题要用long long!!


#include <iostream>
#include <cstring>
#include <algorithm>
#include <cstdio>
#include <cstdlib>
#define LL long long
using namespace std;
struct edge
{
	int y,ne;
}e[5005];
int cnt=0,tot=0,h[105],p[15],c[605],q[15],a[105],n,k;
LL f[15][105][105];
bool Ok(int x)
{
	int s=x;
	for (int i=1;i<=n;i++)
	{
		p[i]=x&1;
		if (p[i]) c[s]++;
		x>>=1;
	}
	for (int i=2;i<=n;i++)
	{
		if (p[i]&&p[i-1]) return false;
	}
	return true;
}
void Add(int x,int y)
{
	tot++;
	e[tot].y=y;
	e[tot].ne=h[x];
	h[x]=tot;
}
bool Can(int x,int y)
{
	for (int i=1;i<=n;i++)
	{
		p[i]=x&1;
		x>>=1;
	}
	for (int i=1;i<=n;i++)
	{
		q[i]=y&1;
		y>>=1;
	}
	for (int i=1;i<=n;i++)
		if (q[i]&&(p[i]||p[i-1]||p[i+1])) return false;
	return true;
}
int main()
{
        scanf("%d%d",&n,&k);
	if (k==0)
	{
		cout<<0<<endl;
		return 0;
	}
	cnt=0;
	for (int i=0;i<(1<<n);i++)
		if (Ok(i)) 
			a[++cnt]=i,f[1][c[i]][cnt]=1LL;
	for (int i=1;i<=cnt;i++)
		for (int j=1;j<=cnt;j++)
			if (Can(a[i],a[j])) Add(i,j);
	LL ans=0LL;
	for (int i=2;i<=n;i++)
		for (int now=1;now<=cnt;now++)
			for (int u=c[a[now]];u<=k;u++)
			    for (int j=h[now];j;j=e[j].ne)
					f[i][u][now]+=f[i-1][u-c[a[now]]][e[j].y];
	for (int i=1;i<=cnt;i++)
		ans+=f[n][k][i];
	cout<<ans<<endl;
	return 0;
}

技术分享

【BZOJ 1087】 [SCOI2005]互不侵犯King