状压DP之互不侵犯

Posted soda-ma

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了状压DP之互不侵犯相关的知识,希望对你有一定的参考价值。

题目描述

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

输入格式

只有一行,包含(N,K)两个数 。

输出格式

所得方案数。

样例

样例输入

3 2

样例输出

16

思路

我们可以想到,对于当前行的影响有当前行的状态,上一行的状态(因为国王的攻击范围可以从上一行包括到这一行),以及当前行的国王数,那么我们可以用一个三维数组(f[n][k][1<<n-1]),用来代表第一维代表前(i)行((1<i<=n)),第二维代表在前(i)行放(j(0<=j<=k))个国王,第三维代表第(i)行的状态,对于f数组的初始化,只需要将(f[0][0][0])初始化为1即可;
对于上一行的判断,我们现在用S表示当前行状态,用s表示上一行状态,那我们就有(if(S&s || (S<<1)&s || (S>>1)&s)continue),显然,我们还应该对当前行以及上一行进行判断(当前行和上一行的国王不能),显然有(if((s<<1)&s)continue),(if((S>>1)&S)continue);
对于当前行的状态我们有(f[i][j][S]+=f[i-1][j-Q(S)][s])(Q函数用来求改状态下的国王个数,即1的个数,需要用到lowbit)。

代码



#include<bits/stdc++.h>
using namespace std;
const int maxn=(1<<9)-1;
long long f[10][100][maxn];
int lowbit(int x){
	return x&-x;
}
int Q(int x){
	int cnt=0;
	for(int i=x;i;i-=lowbit(i))cnt++;
	return cnt;
	
}
int main(){
	int n,k;
	cin>>n>>k;
	int maxs=1<<n;
	f[0][0][0]=1;
	for(int i=1;i<=n;i++){//枚举每一行
		for(int S=0;S<maxs;S++){//枚举当前行状态
			if((S>>1)&S)continue;
			for(int s=0;s<maxs;s++){//枚举上一行的状态
				if((s<<1)&s)continue;//去掉上一行排斥情况(可以无)
				if(S&s || (S<<1)&s || (S>>1)&s)continue;//去掉当前行去上一行冲突情况
				for(int j=Q(S);j<=k;j++){//枚举前i行的国王个数
					f[i][j][S]+=f[i-1][j-Q(S)][s];
				}
			}
		}	
	}
	long long ans=0;
	for(int i=0;i<=maxs;i++){
		ans+=f[n][k][i];
	}
	cout<<ans;

}




以上是关于状压DP之互不侵犯的主要内容,如果未能解决你的问题,请参考以下文章

[SCOI2005]互不侵犯 (状压$dp$)

状压dp互不侵犯KING

[SCOI2005]互不侵犯(状压Dp)

[状压DP]SCOI2005 互不侵犯King

题解[SCOI2005] 互不侵犯 (状压DP)

1896 [SCOI2005]互不侵犯 状压dp