C. Boboniu and Bit Operations(状压)

一个超级显然的状压

可惜我昨天没打

这题有个很简单的思维解法,想看戳我哦,比状压简单很多

言归正传,反正思维的解法我没想到,用状压草过去的,状压毕竟具有普适性

定 义 d p [ i ] [ j ] 为 构 造 好 第 i 个 c i 后 , 前 i 个 c 元 素 相 或 运 算 是 否 可 能 得 到 二 进 制 j 定义dp[i][j]为构造好第i个c_i后,前i个c元素相或运算是否可能得到二进制j dp[i][j]ici,icj

这 个 j 的 范 围 就 是 [ 0 , 1 < < 9 ] 嘛 , 因 为 或 和 与 不 会 改 变 二 进 制 范 围 这个j的范围就是[0,1<<9]嘛,因为或和与不会改变二进制范围 j[0,1<<9],

那 么 对 于 当 前 的 a i , 枚 举 一 个 b q 构 造 成 此 时 的 c i 那么对于当前的a_i,枚举一个b_q构造成此时的c_i ai,bqci

再 来 一 层 f o r 循 环 枚 举 与 运 算 完 c i 后 的 二 进 制 j 再来一层for循环枚举与运算完c_i后的二进制j forcij

再 来 一 层 f o r 循 环 枚 举 二 进 制 d 表 示 要 从 d p [ i − 1 ] [ d ] 转 移 而 来 再来一层for循环枚举二进制d表示要从dp[i-1][d]转移而来 forddp[i1][d]

于是转移方程是

d p [ i ] [ j ] = d p [ i ] [ j ] dp[i][j]=dp[i][j] dp[i][j]=dp[i][j] | d p [ i − 1 ] [ d ] dp[i-1][d] dp[i1][d]

中间的竖线表示或运算

意 思 是 , 只 要 存 在 一 个 d 使 得 d p [ i − 1 ] [ d ] 可 行 , 那 么 d p [ i ] [ j ] 也 可 行 意思是,只要存在一个d使得dp[i-1][d]可行,那么dp[i][j]也可行 ,d使dp[i1][d],dp[i][j]

于是我们可以写下如下代码

dp[0][0]=1;
for(int i=1;i<=n;i++)
for(int q=1;q<=m;q++)
{
	int s = (a[i]&b[q]) ;//当前构造的c[i]
	for(int j=0;j<=(1<<9);j++)//本状态 
	{
		 if( (j&s)!=s )	continue;//本状态由于或上了c[i],二进制一定包含c[i]
		 for(int d=0;d<=j;d++)//上一状态d|s=j,所以不可能大于j
		 {
		 	if( (d|s)!=j )	continue;
		 	dp[i][j]|=dp[i-1][d];
		 }
	}
}

拿起小拇指一算复杂度,哇呜呜

2 9 ∗ 2 9 ∗ 200 ∗ 200 = . . . . . . . . 2^9*2^9*200*200=........ 2929200200=........

但 是 仔 细 看 , 枚 举 d 的 那 个 循 环 , 很 多 工 作 是 不 需 要 的 但是仔细看,枚举d的那个循环,很多工作是不需要的 ,d,

现 在 已 知 s 和 j , 我 们 需 要 枚 举 的 都 是 s ∣ d = = j 的 那 些 d 现在已知s和j,我们需要枚举的都是s|d==j的那些d sj,sd==jd

d或运算s变成j

说明 s s s只贡献了自己的某个子集给 j j j,设贡献了 y y y

然后 y y y一定是 s s s的子集,所以直接枚举子集就好了

这样来枚举数 v v v的子集

for(int w=v;w;w=(w-1)&v )
#include <bits/stdc++.h>
using namespace std;
int n,m;
int f[209][1<<10],a[209],b[209];
int main()
{
	//定义f[i][j]表示前i个c[i]或运算能否得到二进制j
	cin >> n >> m;
	for(int i=1;i<=n;i++)	cin >> a[i];
	for(int i=1;i<=m;i++)	cin >> b[i];
	f[0][0] = 1;
	for(int i=1;i<=n;i++) 
	for(int j=1;j<=m;j++)//枚举这次选择的b[j]
	{
		int v = a[i]&b[j];//当前选的是v 
		for(int u=0;u<(1<<9);u++)//枚举当前状态f[i][u]
		{
			if( (u&v)!=v )	continue;		
			for(int w=v;w;w=(w-1)&v )//枚举v的子集贡献
				f[i][u] |= f[i-1][u^w];	
			f[i][u] |= f[i-1][u];
		}
	} 
	for(int i=0;;i++)
		if( f[n][i] ) { cout << i; return 0; }
}
评论 17
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值