630 字
3 分钟
BZOJ 2064 分裂 状压DP
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,
题解
状压DP 首先可以知道用次一定可以实现
所以我们考虑减少次数。
如果原集合能分出来一块与新集合中的一块大小相同。
那么这是一个子问题, 他的答案也是
同样剩下的集合也是如此。 这样我们就把答案减小了2。
然后可以看出我们需要尽可能多的将集合分为几部分。 每部分对应相等。
这可以用状压DP来解决。
可以将新集合中所有值设为相反数, 这样就可以求和为0就可以了。
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
int a[100];
int S[(1 << 20) + 1], f[(1 << 20) + 1];
int main()
{
int n, m;
int cnt = 0;
scanf ("%d", &n);
for (cnt = 1; cnt <= n; cnt++)
scanf ("%d", &a[cnt]);
scanf ("%d", &m);
n += m;
for (; cnt <= n; cnt++)
scanf ("%d", &a[cnt]), a[cnt] = -a[cnt];
int N = (1 << n) - 1;
for (int i = 1; i <= N; i++)
{
int j = 0;
for (j = 1; j <= n; j++)
if (i & (1 << (j - 1)))
break;
S[i] = S[i ^ (1 << (j - 1))] + a[j];
for (j = 1; j <= n; j++)
if (i & (1 << (j - 1)))
f[i] = max(f[i], f[i ^ (1 << (j - 1))] + (!S[i]));
}
printf ("%d", n - f[N] * 2);
}