P1896 [SCOI2005] 互不侵犯 题解

[SCOI2005] 互不侵犯 - 洛谷

看了很多题解都没看懂,直到我看到了bilibili董晓算法的视频我才理解。

这道题要用到位运算和多重背包。先说一下位运算:

!(i&i>>1)代表将i右移一维看看和原来的i有没有重叠的1,如果有,那么结果不为零,前面的感叹号取反后不为0,if语句就为假,那他有什么用呢?作用就是判断i的二进制是否有无相邻的1。

!(a&b)表示判断a和b的二进制有无相同位置的1,

!(a<<1&b)表示a的右边有没有1,如果有的话会和b的二进制的某一位重合导致结果为真,取反后结果为假,

!(a>>1&b)同理。这样我们就可以用二进制来筛选符合条件的国王的排列了。

#include <iostream>
#include <algorithm>
using namespace std;
#define maxn 100
#define int long long
int N,K;
int num[maxn];
int st[maxn],top;
int f[10][100][8192];
signed main(){
	cin>>N>>K;
	for(int i=0;i<(1<<N);i++){
		if(!(i&i>>1)){
			st[top++]=i;
			for(int j=0;j<N;j++){
				num[i]+=(i>>j&1);
			}
		}
	}
	f[0][0][0]=1;
	for(int i=1;i<=N;i++){
		for(int j=0;j<=K;j++){
			for(int a=0;a<top;a++){
				for(int b=0;b<top;b++){
					if((j-num[st[a]]>=0)&&!(st[a]&st[b])&&!(st[a]<<1&st[b])&&!(st[a]>>1&st[b])){
						f[i][j][a]+=f[i-1][j-num[st[a]]][b];
					}
				}
			}
		}
	}
	int ans=0;
	for(int i=0;i<top;i++){
		ans+=f[N][K][i];
	}
	cout<<ans;
	return 0;
}

这里 f[N][K][S]代表的是当我处于第N行并且目前已经选择了K个国王时,国王的二进制排列为S的方案数。

洛谷的题解区总是喜欢谜语人,他们的说法是这样的:f[N][K][S]代表第N行选择了K个国王状态为S的方案数,对于新手来说理解起来简直费劲。他并没有说清楚“状态为S”是什么意思。

S的状态需要预处理出来,从0枚举到2^N-1,然后先把二进制有相邻的位都是1的数筛掉,剩下的放到数组st里面top++,之后计算每个符合条件的S中有多少个1,也就是num数组,for循环j,如果0100100这里面有1,那么当i>>j&1结果就是1,num[i]++就好了。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值