BZOJ2064 分裂

Description

中国历史上上分分和和次数非常多。通读中国历史的WJMZBMR表示毫无压力。 同时经常搞OI的他把这个变成了一个数学模型。 假设中国的国土总和是不变的。 每个国家都可以用他的国土面积代替, 有两种可能,一种是两个国家合并为1个,那么新国家的面积为两者之和。 一种是一个国家分裂为2个,那么2个新国家的面积之和为原国家的面积。 WJMZBMR现在知道了很遥远的过去中国的状态,又知道了中国现在的状态,想知道至少要几次操作(分裂和合并各算一次操作),能让中国从当时状态到达现在的状态。数据保证有解。

Input

第一行一个数 \(n1\),表示当时的块数,接下来 \(n1\) 个数分别表示各块的面积。 第二行一个数 \(n2\),表示现在的块,接下来 \(n2\) 个数分别表示各块的面积。

\(n1, n2 \leq 10\)

Output

一行一个数表示最小次数。

分析

状压dp好题。

如果说直接暴力的合并和分裂的话,最多需要的次数是 \(n+m−2\) 次,其实可以这个基础上进行优化呢。例如 $ 4,6,9,2$ -> \(10,5,6\) 完全可以把 \(4,6\) 单独处理 ,不用将他们再跟\(2,7\) 合并了,也不用额外的删除,答案也可以 \(-2\)

于是发现如果上下可以被分成值相等的 \(k\) 块,那么总的次数是 \(n+m-2*k\)

然后现在就是要求 \(k\) 的最大值了。

用二进制表示某一个数选或不选。如果一个数是 \(n1\),令它值为正,\(n2\) 的则值为负。

然后如果上面的一块和下面的一块值对应相等的话,就是所有的值加起来为 \(0\)

这样如果某一个子集的和为 \(0\) ,就说明它可以互相变化
于是令 \(dp_i\)表示选的状态为 \(i\) 时的最大的 \(k\) ,因为只有在 \(sum_i=0\) 时,\(dp_i++\),其他状态都只能直接转移。

就可以使用状压dp了。

\(code\)

#include<bits/stdc++.h>
#define N 1<<22
using namespace std;
int read(){
	int x=0,f=1;char ch=getchar();
	while(ch<'0'||ch>'9'){if(ch=='-')f=-f;ch=getchar();}
	while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
	return x*f;
}
int n,n1,n2;
int sum[N],dp[N];
int main(){
	n1=read();for(int i=1;i<=n1;++i) sum[1<<i-1]=read();
	n2=read();for(int i=1;i<=n2;++i) sum[1<<(i+n1-1)]=-read();
	n=1<<(n1+n2);
	for(int i=1;i<n;++i){
		int tmp=i&-i;sum[i]=sum[tmp]+sum[i-tmp];
		for(int j=1;j<=n1+n2;++j){
			if(i&(1<<j-1)) dp[i]=max(dp[i],dp[i^(1<<j-1)]);
		}
		if(!sum[i]) dp[i]++;
	}
	printf("%d\n",n1+n2-2*dp[n-1]);
	return 0;
}
posted @ 2022-10-30 12:05  Aurora-JC  阅读(36)  评论(0编辑  收藏  举报