【状压DP】SCOI2005-洛谷P1896-互不侵犯 (状压例题)

【状压DP】SCOI2005-洛谷P1896-互不侵犯 (状压例题)

标签(空格分隔): 状压DP


好久没写博客了,真的爽(误)

题目:

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

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

输出格式
所得的方案数

输入
3 2
输出
16

思路:

状压的入门题,做此题前建议先行做玉米地那道例题。这道题与玉米地的区别在于状态多了一维(为什么多一维:1.玉米地那道题可以理解为国王只要可以就能无限放 2.玉米地要求的是最大放置个数,这道题求得是方案数),但这就已经有点麻烦了。
设定f[i][j][k],表示第i行状态为j时总共放了k个国王(举个例子:f[2][10100(二进制)][b[10100(二进制)]表示第2行状态为10100(第2行第3列和第5列放国王)的方案数)。

预处理:

    int Lowbit(int x){return x & -x;};
    -----------------------------------------------------------------------------
	int maxs=1<<n;//所有可能的二进制
    for(int i=0;i<maxs;i++){
    	if(!((i<<1)&i)){//正左正右不能放国王
    		a[++ans]=i;//a[ans]表示第ans个状态的十进制
    		for(int s=i;s;s-=Lowbit(s)){
    			b[ans]++;//表示第ans个状态放了多少个国王(1的个数)
			}
		}
	}
	for(int i=1;i<=ans;i++){//预处理第一行的状态
		if(b[i]<=m)f[1][i][b[i]]=1;//能放就放
	}

主代码

	for(int i=2;i<=n;i++){
		for(int j=1;j<=ans;j++){//枚举这一行的每一个状态
			for(int k=1;k<=ans;k++){//枚举上一行的每一个状态
				if((a[j]&a[k])||((a[j]<<1)&a[k])||(a[k]&(a[j]>>1)))continue;//正上、左上、右上都不能放
				//预处理的时候已经把正左正右处理了
				for(int s=1;s<=m;s++){//枚举已有的国王数量
					if(b[j]+s<=m)f[i][j][b[j]+s]+=f[i-1][k][s];//能放就放,再把方案数相加
				}
			}
		}
	}

输出

for(int i=1;i<=n;i++){
		for(int j=1;j<=ans;j++){
			sum+=f[i][j][m];
		}
	}
	cout<<sum;

OVER

原文地址:https://www.cnblogs.com/614685877--aakennes/p/12733012.html