[BZOJ]2064 分裂 状压DP 思路神题

2064: 分裂

Time Limit: 10 Sec   Memory Limit: 64 MB
Submit: 652   Solved: 401
[ Submit][ Status][ Discuss]

Description

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

Input

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

Output

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

Sample Input

1 6
3 1 2 3

Sample Output

2
数据范围:
对于100%的数据,n1,n2<=10,每个数<=50
对于30%的数据,n1,n2<=6,

HINT

Source

[ Submit][ Status][ Discuss]


HOME Back

    又是一道思路好题... 这道题有用到了一种分治的思想, 将一个问题分成几个小问题. 

  设原集合为s1, 目标集合为s2. 首先我们知道, 最坏情况下只需要合并n + m - 2次. 只需要先n-1合并成一个大数, 再m-1次分裂即可. 但是显然如果s1有个子集和s2有个子集和相等, 那么那么就可以将s1拆成这个子集和这个子集的补集, 然后再按最坏情况分裂, 这就是n + m - 4. 同理, 如果s1有k个集合对应s2的k个集合, s1的这k个子集分别分裂合并成对应的k个s2子集, 也就成了s2了, 同理只需要n + m - 2 * k次. 那么我们一个贪心思路就是把s1分成尽量多的集合和s2配对.

  先姑且不讨论贪心的正确性, 我们讨论一下怎么算最多有多少个集合能够配对. 设f[i]表示为s1, s2分别选取某些数最多能配对多少. 为什么只用i一个二进制数就能表示?只需要把s2的状态放到s1之后就可以了. 那么转移的话就是f[i] = max(f[i], f[i ^ pw[j - 1]]); 就是s1或者s2你拿走一个数之后最多能配对多少. 为什么不用分开枚举s1, s2的子集来转移呢? 因为假设sum[s1] == sum[s2]的话, 假设最大匹配k, 我拿走一个元素一定匹配数-1. 那么也就是说我可以从一个拿走当前一个数的状态转移过来然后+1(前提是sum[s1] == sum[s2], 很明显只要相等一定能再增1对). 这里将s2设为负数只有sum[i]为0就说明当前两集合选择的数sum相等.

  那么贪心的正确性呢? 我们发现贪心下的最优状态一定是分成了最多k个子集. 那么每个集合变化成对应配对的集合就需要这个子集大小+对应子集大小-2. 有没有可能更少呢. 现在我们已经分成了k个子问题(即子集), 要保证每个子问题最优即可. 考虑一个子问题. 假设某个子集ss大小为p, 对应s2里的子集gg的大小为q. 最坏情况是需要p+q - 2次操作. 现在我们来证明这也是最优的操作. 我们原来最坏情况下的操作是先合并再分裂. 此时我们可以想象可能最优方法可能是合并分裂合并分裂合并合并夹杂. 那么为了证明此时最坏就是最优, 我们需要简化情况--有没有可能合并分裂合并分裂 等价于 合并合并.... 分裂分裂...即先合并再分裂, 像最坏情况那样呢? 是的. 因为比如说我先把a分裂成a1, a2, 再把a2和b合并. 这样的操作等价于合并a和b, 再分离成a1和a2+b, 操作数相等. 我们现在已经知道操作顺序相邻之间分裂可以与之后的合并互换. 那么一个合并分裂合并合并分裂合并的操作序列一定可以转化成合并合并... 分裂分裂即先合并再分裂的序列. 那么任意操作序列都可以变成最坏情况下的操作模式. 那么我们假设最优操作序列比我们最坏情况优, 肯定是合并次数少(因为要变成的都是gg, 大小一样, 合并次数少也就意味着分裂次数少). 那么为什么合并会少呢? 这就说明合并到某个时候没必要再合并了. 我们设最优情况不必再合并准备开始分裂的时候, 剩余没合并过得数的集合为s3 .因为只能分裂, 这就说明了s3的和等于gg里的某个子集. 但是我们已经保证当前全局最优情况已经分的不能再分, 这个s1的子集ss不可能再有子集s3与gg里的子集相等, 不然还可以再分裂. 所以说最优情况只能一直合并到只剩一个数... 这就是最坏情况的方式啊?? 所以说贪心正确.

  整道题的思路: 考虑最坏情况 -> 发现子集对应相等可以减少次数 -> 考虑贪心分成尽可能多的子集 -> 发现贪心正确必须要保证每个子集分裂最优 -> 证明贪心, 发现就是最坏情况的合并次数 -> 贪心正确, 证得最优答案就是n + m - 2 * k (k为最大配对数) -> 状压求解.

#include<stdio.h>
#include<algorithm>
using namespace std;
int n, m, num, tmp, f[1 << 21], sum[1 << 21], pw[21];
int main() {
	register int i, j;
	pw[0] = 1;
	for (i = 1; i <= 20; ++ i) pw[i] = pw[i - 1] << 1;
	scanf("%d", &n);
	for (i = 1; i <= n; ++ i) scanf("%d", &sum[pw[i - 1]]);	
	scanf("%d", &m);
	for (i = 1; i <= m; ++ i){
		scanf("%d", &sum[pw[n + i - 1]]); 
		sum[pw[n + i - 1]] = - sum[pw[n + i - 1]];
	}
	num = pw[n + m] - 1;
	for (i = 1; i <= num; ++ i) {
		tmp = i & -i;
		sum[i] = sum[tmp] + sum[i ^ tmp];
		for (j = 1; j <= n + m; ++ j)
			if (i & pw[j - 1])
				f[i] = max(f[i], f[i ^ pw[j - 1]]);
		if (!sum[i]) f[i] ++;
	}
	printf("%d\n", n + m - (f[num] << 1));
	return 0;
}


  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值