BZOJ1087: [SCOI2005]互不侵犯King(状态压缩动态规划)

www.cnblogs.com/shaokele/


1087: [SCOI2005]互不侵犯King##

Time Limit: 10 Sec Memory Limit: 162 MB

Description###

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

Input###

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

Output###

  方案数。

Sample Input###

  3 2

Sample Output###

  16

题目地址 BZOJ1087: [SCOI2005]互不侵犯King
题目大意:题面已经很简洁了(逃


题解:####

  先预处理出每一层状态与下一层状态之间的关系,dp转移
  状态: \(dp[i][j][k]\) 表示第 \(i\) 层取了 \(j\) 个国王,这层状态为 \(k\)
  转移:详见标程
  c++运算优先级一定要注意啊!


#include <cstdio>
#define ll long long
using namespace std;
const int N=512;
int n,m,all;
int cnt[N];
ll dp[10][100][N];
bool fl[N],mat[N][N];
void init(){
	for(int i=0;i<=all;i++)
		if((i&(i>>1))==0){            //排除同层国王的冲突情况 
			int s=0;
			for(int x=i;x;x>>=1)      //记录一层放国王的数量 
				s+=x&1;
			cnt[i]=s;fl[i]=1;
		}
	for(int i=0;i<=all;i++)if(fl[i])
		for(int j=0;j<=all;j++)if(fl[j])
			if(((i&j)==0) && ((i&(j>>1))==0) && ((j&(i>>1))==0))
				mat[i][j]=1;          //处理状态的转移 
}
int main(){
	scanf("%d%d",&n,&m);
	all=(1<<n)-1;
	init();
	for(int i=0;i<=all;i++)
		if(fl[i])
			dp[1][cnt[i]][i]=1;
	for(int k=1;k<n;k++)
		for(int i=0;i<=all;i++)if(fl[i])
			for(int j=0;j<=all;j++)if(fl[j])               //枚举两层的状态 
				if(mat[i][j])
					for(int p=cnt[i];p+cnt[j]<=m;p++)      //p表示到第i层去了几个国王 
						dp[k+1][p+cnt[j]][j]+=dp[k][p][i]; //转移 
	ll ans=0;
	for(int i=0;i<=all;i++)
		ans+=dp[n][m][i];
	printf("%lld\n",ans);
	return 0;
}
posted @ 2018-04-23 10:07  skl_win  阅读(120)  评论(0编辑  收藏  举报
Live2D