【BZOJ1087】[SCOI2005] 互不侵犯King(状压DP)

点此看题面

大致题意: N × N N×N N×N的棋盘里面放 K K K个国王,使他们互不攻击,共有多少种摆放方案(国王能攻击到它周围的8个格子)。


状压 D P DP DP

一看到这道题我就想到了经典的八皇后问题,但是,这道题其实可以用状压DP来做。

我们可以发现,影响该行国王摆放方法的只有上一行国王的摆放方式,因此,对于第 i i i行,我们只需要知道第 i − 1 i-1 i1行的国王的摆放方式即可。所以,我们可以用 f [ i ] [ j ] f[i][j] f[i][j]来记录第 i i i行,国王摆放方式为 j j j的方案数即可(这里将摆放方式状态压缩成一个数)。

注意要先预处理一下对于某一行的一种摆放方式是否合法且用了多少个国王。


代码
#include<bits/stdc++.h>
#define LL long long
#define N 9
using namespace std;
int n,m,could[(1<<N)+5],tot[(1<<N)+5];
LL f[N+5][N*N+5][(1<<N)+5];
inline char tc()
{
	static char ff[100000],*A=ff,*B=ff;
	return A==B&&(B=(A=ff)+fread(ff,1,100000,stdin),A==B)?EOF:*A++;
}
inline void read(int &x)
{
	x=0;int f=1;char ch;
	while(!isdigit(ch=tc())) f=ch^'-'?1:-1;
	while(x=(x<<3)+(x<<1)+ch-'0',isdigit(ch=tc()));
	x*=f;
}
inline void write(LL x)
{
	if(x<0) putchar('-'),x=-x;
	if(x>9) write(x/10);
	putchar(x%10+'0');
}
inline void Start()//预处理每种摆放方案是否合法且用了几个国王
{
	register int i;
	for(i=0;i<(1<<n);++i)
	{
		could[i]=1,tot[i]=0;
		for(int num=i,lst=0;num;lst=num&1,num>>=1)
		{
			if(num&1)//若当前一位摆放了国王
			{
				if(lst) could[i]=0;//若前一位摆放了国王,则此方案不合法
				++tot[i];//将国王的使用数量加1
			}
		}
	}
}
inline int check(int x,int y)
{
	return !((x&y)||((x<<1)&y)||(x&(y<<1)));//比较两行的国王是否会攻击到对方
}
int main()
{
	register int i,j,k,l;
	read(n),read(m),Start(),f[0][0][0]=1;
	for(i=1;i<=n;++i)//核心代码
		for(j=0;j<(1<<n);++j)
			if(could[j]&&tot[j]<=m)
				for(k=tot[j];k<=m;++k) 
					for(l=0;l<(1<<n);++l)
						if(could[l]&&check(j,l)) f[i][k][j]+=f[i-1][k-tot[j]][l];//进行转移,计算该行当前摆放方式的方案数
	LL ans=0;
	for(j=0;j<(1<<n);++j) ans+=f[n][m][j];//统计答案
	return write(ans),0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值