BZOJ2064分裂 题解

发布时间 2023-08-01 16:01:53作者: onlycre

link

通过数据范围,容易想到应该是将状态压缩。我们发现合并操作是容易简单描述的,而分裂比较复杂。分析能得到,初始的状态要达到结束状态,我们可以先合并再分裂,这样做答案不会更差(想想应该很容易理解),由于最后几次都是分裂操作,等价于将结束状态合并。现在问题变成了:有两个状态,我们可以分别对他们进行合并的操作,求最少操作次数,答案上界是 \(n+m-2\),如果在合并过程中出现了相等的元素,就可以减少 \(2\) 的花费,我们需要最大化减少的量。使用状压dp,记 \(f_{i,j}\) 表示两个集合的状态分别是 \(i\)\(j\) 的最大的相等元素个数,转移过程中找最大即可。

code:

#include<cstdio>
#include<algorithm>
#include<cmath>
#include<vector>
#include<queue>
#include<cstring>
#define rep(i,a,b) for(int i=a;i<=b;++i)
#define per(i,a,b) for(int i=a;i>=b;--i)
#define rpe(i,x) for(int i=_he[x];i;i=_ne[i])
#define lb(x) (x&(-x))
using namespace std;

int n,m,f[1<<20],s[1<<20];

int main()
{
	scanf("%d",&n);
	rep(i,0,n-1)
	{
		int v;scanf("%d",&v);
		s[1<<i]=v;
	}
	scanf("%d",&m);
	rep(i,0,m-1)
	{
		int v;scanf("%d",&v);
		s[1<<n+i]=-v;
	}
	
	rep(i,1,(1<<n+m)-1)s[i]=s[i^lb(i)]+s[lb(i)];
	
	int ans=0;
	rep(i,1,(1<<n+m)-1)
	{
		rep(j,0,n+m-1)
			if((i>>j)&1)f[i]=max(f[i],f[i^(1<<j)]);
		if(!s[i])++f[i];
		ans=max(ans,f[i]);
	}
	printf("%d\n",n+m-2*ans);
	return 0;
}