[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;
}
?