[BZOJ 2064]分裂

2064: 分裂

Time Limit: 10 Sec  Memory Limit: 64 MB
Submit: 572  Solved: 352
[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

题解

这个题的思路异常地清奇...

首先看到数据范围很容易想到暴搜或者状压DP 2333

然后我们可以想到, 对于一个 $n$ 块的情况要想办法分裂合并为一个 $m$ 块的情况, 我们有保证可以分裂合并完成的策略: 通过 $n-1$ 次合并将面积合并为一整坨, 然后再通过 $m-1$ 次分裂将面积分为目标状态, 总操作数 $n+m-2$ . 然后我们需要想办法优化这个过程.

然后我们发现, 如果初始状态中挑出 $k_0$ 块的面积刚好等于目标状态中的 $k_1$ 块的面积, 我们可以单独让这几块通过 $k_0+k_1-2$ 次操作达到目标状态, 剩余的 $n-k_0$ 块与 $m-k_1$ 块可以通过 $n-k_0+m-k_1-2$ 次分裂合并达到目标状态, 总操作数会变为 $n+m-4$ 次.

也就是说我们如果每找到这样的一组对应关系, 就可以通过分组分裂合并减少 $2$ 次分裂合并的操作. 这样的组越多则所需操作数越少.

所以我们可以定义 $dp_i$ 为 $i$ 所代表的状态中的对应关系数的最大值. 另外再开一个辅助数组记录面积和来判断是否面积相等. 这里可以将目标状态的面积值定义为负, 这样就可以通过求和判断是否为 $0$ 来判定两块的面积是否相等了. 如果相等则说明找到了新一对对应关系(因为状态是唯一的).

至于转移, $dp_i$ 可以从状态 $i$ 任意减少一个元素后的状态转移. 取一下 $max$ 就好.

找到新一组对应关系后给 $dp$ 数组中对应该状态的值 $+1$ 就可以.

转移后得到的最终答案为 $n+m-2dp_{2^{n+m}-1}$ (因为每一对都减少了 $2$ . 整个整体也可以看成一对.)

参考代码

GitHub

 1 #include <cstdio>
 2 #include <cstring>
 3 #include <cstdlib>
 4 #include <iostream>
 5 #include <algorithm>
 6 
 7 const int MAXN=22;
 8 
 9 int n;
10 int n1,n2;
11 int size[MAXN];
12 int dp[1<<MAXN];
13 int sum[1<<MAXN];
14 
15 int main(){
16     scanf("%d",&n1);
17     for(int i=0;i<n1;i++){
18         scanf("%d",size+i);
19     }
20     scanf("%d",&n2);
21     for(int i=0;i<n2;i++){
22         scanf("%d",size+n1+i);
23         size[n1+i]=-size[n1+i];
24     }
25     n=n1+n2;
26     for(int i=1;i<(1<<n);i++){
27         for(int j=0;j<n;j++){
28             if((i&(1<<j))!=0){
29                 dp[i]=std::max(dp[i],dp[i^(1<<j)]);
30                 sum[i]+=size[j];
31             }
32         }
33         if(sum[i]==0)
34             dp[i]++;
35     }
36     printf("%d\n",n-2*dp[(1<<n)-1]);
37     return 0;
38 }
Backup

picpic?picpic!

 

posted @ 2017-10-20 15:18  rvalue  阅读(270)  评论(0编辑  收藏  举报