C. Boboniu and Bit Operations(状压)

一个超级显然的状压

可惜我昨天没打

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

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

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

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

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

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

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

于是转移方程是

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

中间的竖线表示或运算

, d 使 d p [ i 1 ] [ d ] , d p [ i ] [ j ] 意思是,只要存在一个d使得dp[i-1][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=........

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

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

d或运算s变成j

或运算嘛,只有当d没有某一位的二进制而s有这位二进制才会使得d的这一位变成1

d ? 但是贡献给d哪几位二进制呢?

对于s的某一位是1的二进制有选和不选两种可能

d f s , v e c dfs爆搜出所有情况,存入vec数组中即可

v e c s d 那么现在vec存的是s贡献给d的二进制

y s d , d s = j 设y是s贡献给d的二进制数,那么d|s=j

解得 d = ( j s ) d=(j异或s)

当然这貌似属于sos枚举子集的状压,你有兴趣可以去搜一下

#include <bits/stdc++.h>
using namespace std;
int n,m,a[209],b[209];
int dp[209][1<<10];
vector<int>vec[209][209];
void dfs(int bit,int temp,int i,int j,int he)
{
	if( bit==(1<<9) )
	{
		vec[i][j].push_back(he);
		return;
	}
	if( bit&temp )
	{
		dfs(bit*2,temp,i,j,he);
		dfs(bit*2,temp,i,j,he+bit);
	}
	else	dfs(bit*2,temp,i,j,he);;
}
int main()
{
	cin >> n >> m;
	for(int i=1;i<=n;i++)	cin >> a[i];
	for(int j=1;j<=m;j++)	cin >> b[j];
	for(int i=1;i<=n;i++)
	for(int j=1;j<=m;j++)
	{
		int temp = (a[i]&b[j]);
		dfs(1,temp,i,j,0);//爆搜 
	}
	dp[0][0]=1;
	for(int i=1;i<=n;i++)
	for(int q=1;q<=m;q++)
	{
		int s = (a[i]&b[q]) ;
		for(int j=0;j<=(1<<9);j++)//本状态 
		{
			 if( (j&s)!=s )	continue;
			 for(int d=0;d<vec[i][q].size();d++)//直接枚举贡献哪些二进制 
			 {
			 	int last=(j^vec[i][q][d]);//上一个状态 
			 	dp[i][j]|=dp[i-1][last];
			 }
		}
	}
	int ans=0;
	for(int i=0;i<=(1<<9);i++)
	if( dp[n][i])
	{
		cout << i;
		return 0;
	}
}

猜你喜欢

转载自blog.csdn.net/jziwjxjd/article/details/107971041