[BZOJ2064]分裂
Posted xjr_01
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了[BZOJ2064]分裂相关的知识,希望对你有一定的参考价值。
[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 2147483647 int 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; }
以上是关于[BZOJ2064]分裂的主要内容,如果未能解决你的问题,请参考以下文章