【SCOI2005】互不侵犯(状压dp)

一般状压dp的问题是求合理的方案,无数量限制。而此题加入了一个K,所以相应地设f(i,k,S)表示第i行摆k个状态为S的可行方案。

此题又没有障碍物之类,所以只需求一次可行的状态, 只需判断相邻是否可攻击到即可。

然后在上下行转移时,需要考虑到四个角也不能攻击到,上下行国王的左右间隔至少为1,但是靠左的国王可能在上一行也可能在下一行所以要分别判断。

状态转移方程: f(i,k,S)=f(i-1,k-\left | S \right |,T) 。其中S,T需要满足上述关系。

#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
int N,K,state[(1<<13)],num[(1<<13)],cnt=0;
LL f[12][100][(1<<13)]; //注意 long long 

void getstate()
{
	cnt=0;
	int S,Num,i,flag;
	for(S=0;S<(1<<N);S++)
	{
		Num=flag=0;
		for(i=0;i<N;i++)
		{
			if((S&(1<<i))&&(S&(1<<(i+1)))) flag=1; //不能互相攻击到 
			if(flag) break;
			if((S&(1<<i))) Num++;
		}
		if(flag) continue;
		if(Num>K) continue;
		state[++cnt]=S;
		num[cnt]=Num;
	}
}

int main()
{
	cin>>N>>K;
	int i,j,j_up,j_down;
	getstate(); //求一次可行状态 
	for(j_up=1;j_up<=cnt;j_up++)
		f[1][num[j_up]][j_up]=1;  //初始化第一列 
		
	for(i=1;i<N;i++)
	{
		for(j_down=1;j_down<=cnt;j_down++)  //枚举第i列的状态 
		for(j=0;j<=K;j++)  //枚举第i+1列的个数 
		{
			if(num[j_down]>j) continue; 
			for(j_up=1;j_up<=cnt;j_up++) //枚举第i+1列的状态 
			{
				if((state[j_down]&state[j_up])) continue;  //三个判断条件 
				if(((state[j_down]<<1)&state[j_up])) continue;
				if(((state[j_down]>>1)&state[j_up])) continue;
				f[i+1][j][j_down]+=f[i][j-num[j_down]][j_up];
			}
		}
	}
	LL ans=0; 
	for(j_up=1;j_up<=cnt;j_up++)
		ans+=f[N][K][j_up];
	cout<<ans;
	return 0;
}

 

猜你喜欢

转载自blog.csdn.net/WWWengine/article/details/82178959
今日推荐