[BZOJ2064]分裂
试题描述
背景: 和久必分,分久必和。。。 题目描述: 中国历史上上分分和和次数非常多。。通读中国历史的WJMZBMR表示毫无压力。 同时经常搞OI的他把这个变成了一个数学模型。 假设中国的国土总和是不变的。 每个国家都可以用他的国土面积代替, 又两种可能,一种是两个国家合并为1个,那么新国家的面积为两者之和。 一种是一个国家分裂为2个,那么2个新国家的面积之和为原国家的面积。 WJMZBMR现在知道了很遥远的过去中国的状态,又知道了中国现在的状态,想知道至少要几次操作(分裂和合并各算一次操作),能让中国从当时状态到达现在的状态。
输入
第一行一个数n1,表示当时的块数,接下来n1个数分别表示各块的面积。 第二行一个数n2,表示现在的块,接下来n2个数分别表示各块的面积。
输出
一行一个数表示最小次数。
输入示例
1 6 3 1 2 3
输出示例
2
数据规模及约定
对于100%的数据,n1,n2<=10,每个数<=50
对于30%的数据,n1,n2<=6,
题解
设 f(S1, S2) 表示 n1 个数中选择了集合 S1 的数,n2 个数中选择了集合 S2 的数。转移的时候我们枚举 S2 的子集 tS,然后找到和 tS 中元素和相同的 S1 的子集 ttS,那么 f(S1, S2) = min{ f(ttS, tS) + f(S1 ^ ttS, S2 ^ tS) },除此之外,还可以直接把 S1 中所有的数合并起来,然后分裂成 S2 中的数,步数即为 S1 中二进制位数 + S2 中二进制位数 - 2。
至于如何找到“和 tS 中元素和相同的 S1 的子集 ttS”,预处理即可,大力搞一个 vector,令 Set[sum] 存储所有元素和等于 sum 的集合。
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cctype>
#include <algorithm>
#include <vector>
using namespace std;int read() {int x = 0, f = 1; char c = getchar();while(!isdigit(c)){ if(c == '-') f = -1; c = getchar(); }while(isdigit(c)){ x = x * 10 + c - '0'; c = getchar(); }return x * f;
}#define maxn 15
#define maxs 3010
#define maxsum 510
#define oo 2147483647int n1, n2, A1[maxn], A2[maxn];
vector <int> Set[maxsum];
int Sum1[maxs], Sum2[maxs], cbin[maxs];int f[maxs][maxs];
int dp(int S1, int S2) {int& ans = f[S1][S2];if(ans < oo) return ans;if(!cbin[S1] && !cbin[S2]) return ans = 0;if(cbin[S1] == 1 && cbin[S2] == 1) return ans = 0;ans = cbin[S1] + cbin[S2] - 2;for(int tS = S2 - 1 & S2; tS; tS = tS - 1 & S2)for(int i = 0; i < Set[Sum2[tS]].size(); i++) if((S1 | Set[Sum2[tS]][i]) == S1)ans = min(ans, dp(Set[Sum2[tS]][i], tS) + dp(Set[Sum2[tS]][i] ^ S1, tS ^ S2));return ans;
}int main() {n1 = read(); for(int i = 0; i < n1; i++) A1[i] = read();n2 = read(); for(int i = 0; i < n2; i++) A2[i] = read();int all = (1 << n1) - 1;for(int S = 0; S <= all; S++) {int sum = 0; cbin[S] = 0;for(int j = 0; j < n1; j++) if(S >> j & 1) sum += A1[j], cbin[S]++;Set[sum].push_back(S); Sum1[S] = sum;}all = (1 << n2) - 1;for(int S = 0; S <= all; S++) {cbin[S] = 0;for(int j = 0; j < n2; j++) if(S >> j & 1) Sum2[S] += A2[j], cbin[S]++;}for(int S1 = 0; S1 <= (1 << n1) - 1; S1++)for(int S2 = 0; S2 <= all; S2++) f[S1][S2] = oo;printf("%d\n", dp((1 << n1) - 1, all));return 0;
}