Codeforces 581F Zublicanes and Mumocrates - 樹形動態規劃
It‘s election time in Berland. The favorites are of course parties of zublicanes and mumocrates. The election campaigns of both parties include numerous demonstrations on n main squares of the capital of Berland. Each of the n squares certainly can have demonstrations of only one party, otherwise it could lead to riots. On the other hand, both parties have applied to host a huge number of demonstrations, so that on all squares demonstrations must be held. Now the capital management will distribute the area between the two parties.
Some pairs of squares are connected by (n - 1) bidirectional roads such that between any pair of squares there is a unique way to get from one square to another. Some squares are on the outskirts of the capital meaning that they are connected by a road with only one other square, such squares are called dead end squares.
The mayor of the capital instructed to distribute all the squares between the parties so that the dead end squares had the same number of demonstrations of the first and the second party. It is guaranteed that the number of dead end squares of the city is even.
To prevent possible conflicts between the zublicanes and the mumocrates it was decided to minimize the number of roads connecting the squares with the distinct parties. You, as a developer of the department of distributing squares, should determine this smallest number.
The first line of the input contains a single integer n (2 ≤ n ≤ 5000) — the number of squares in the capital of Berland.
Next n - 1 lines contain the pairs of integers x, y (1 ≤ x, y ≤ n, x ≠ y) — the numbers of the squares connected by the road. All squares are numbered with integers from 1 to n. It is guaranteed that the number of dead end squares of the city is even.
OutputPrint a single number — the minimum number of roads connecting the squares with demonstrations of different parties.
Examples input8output
1 4
2 4
3 4
6 5
7 5
8 5
4 5
1input
5output
1 2
1 3
1 4
1 5
2
題目大意
給定一棵有$n$個點的無根樹(度為1的點不為根),保證它的葉節點的個數為偶數。將所有點染成黑白兩種顏色,要求
- 黑的葉節點數等於白的葉節點數
- 有邊相連但顏色不同的點對數最少
問最少的這樣的點對數。
顯然動態規劃。
Solution 1
用$f[i][j][0/1]$表示當前考慮$i$號點,它的子樹內有$j$個葉節點是黑色的最優結果。
轉移是顯然的。
至於時間復雜度為什麽可過?下面解釋一下(為了方便計算,那麽就用子樹$size$來說明吧)
設當前考慮的節點的第$i$個子節點為$s_{i}$。
$\sum_{i = 1}size[s_{i}]\cdot\sum_{j = 1} ^ {i - 1}size[s_{j}] = \sum_{i < j}size[s_{i}]\cdot size[s_{j}]$
然後可以發現對於任意一對節點$\left(u, v\right)$僅對它們的lca有1的貢獻,所以總時間復雜度為$O\left(n^{2}\right)$
Code
1 /** 2 * Codeforces 3 * Problem#581F 4 * Accepted 5 * Time: 139ms 6 * Memory: 198380k 7 */ 8 #include <bits/stdc++.h> 9 using namespace std; 10 typedef bool boolean; 11 #define smin(_a, _b) (_a = min(_a, _b)) 12 13 const int N = 5005; 14 15 int n; 16 vector<int> *g; 17 // 0: black, 1: white 18 int f[N][N][2]; // node, number of the black nodes, the color of this node 19 int temp[N][2]; 20 int root; 21 int clf[N]; 22 int deg[N]; 23 24 inline void init() { 25 scanf("%d", &n); 26 g = new vector<int>[(n + 1)]; 27 for(int i = 1, u, v; i < n; i++) { 28 scanf("%d%d", &u, &v); 29 g[u].push_back(v); 30 g[v].push_back(u); 31 deg[u]++, deg[v]++; 32 } 33 for(root = 1; root < n && deg[root] == 1; root++); 34 } 35 36 void treedp(int node, int fa) { 37 if(deg[node] == 1) { 38 f[node][1][0] = f[node][0][1] = 0; 39 clf[node] = 1; 40 return; 41 } 42 // memset(temp) 43 clf[node] = 0; 44 f[node][0][0] = f[node][0][1] = 0; 45 for (int i = 0; i < deg[node]; i++) { 46 int e = g[node][i]; 47 if (e == fa) continue; 48 treedp(e, node); 49 memset(temp, 0x3f, sizeof(temp)); 50 for (int s1 = clf[node]; ~s1; s1--) { 51 for (int s2 = clf[e]; ~s2; s2--) { 52 smin(temp[s1 + s2][0], f[node][s1][0] + min(f[e][s2][0], f[e][s2][1] + 1)); 53 smin(temp[s1 + s2][1], f[node][s1][1] + min(f[e][s2][0] + 1, f[e][s2][1])); 54 } 55 } 56 clf[node] += clf[e]; 57 for (int j = 0; j <= clf[node]; j++) 58 f[node][j][0] = temp[j][0], f[node][j][1] = temp[j][1]; 59 } 60 } 61 62 inline void solve() { 63 memset(f, 0x3f, sizeof(f)); 64 treedp(root, 0); 65 int k = clf[root] >> 1; 66 int ans = min(f[root][k][0], f[root][k][1]); 67 printf("%d\n", ans); 68 } 69 70 int main() { 71 init(); 72 solve(); 73 return 0; 74 }Slower Version
Solution 2
由於轉移的時候僅和當前節點的顏色和它的父節點的顏色是否相同有關,所以用$f[i][j]$表示當前考慮第$i$號點,它的子樹內有$j$個葉節點是黑色的最優結果。
怎麽轉移呢?
先當父節點顏色和當前節點顏色相同,按照上面的方法進行轉移。
然後考慮將當前子樹內的所有點的顏色反轉,這樣會導致當前點和父節點的顏色不同,答案加1,這樣去更新。
Code
1 /** 2 * Codeforces 3 * Problem#581F 4 * Accepted 5 * Time: 61ms 6 * Memory: 100280k 7 */ 8 #include <bits/stdc++.h> 9 using namespace std; 10 typedef bool boolean; 11 #define smin(_a, _b) (_a = min(_a, _b)) 12 13 const int N = 5005; 14 15 int n; 16 vector<int> g[N]; 17 int f[N][N]; // node, number of the black nodes 18 int root; 19 int clf[N]; 20 int deg[N]; 21 22 inline void init() { 23 scanf("%d", &n); 24 for(int i = 1, u, v; i < n; i++) { 25 scanf("%d%d", &u, &v); 26 g[u].push_back(v); 27 g[v].push_back(u); 28 deg[u]++, deg[v]++; 29 } 30 for(root = 1; root < n && deg[root] == 1; root++); 31 } 32 33 void treedp(int node, int fa) { 34 if(deg[node] == 1) { 35 f[node][1] = 1, f[node][0] = 0; 36 clf[node] = 1; 37 return; 38 } 39 clf[node] = 0; 40 f[node][0] = 0; 41 for (int i = 0; i < deg[node]; i++) { 42 int e = g[node][i]; 43 if (e == fa) continue; 44 treedp(e, node); 45 for (int s1 = clf[node]; ~s1; s1--) { 46 for (int s2 = clf[e]; ~s2; s2--) { 47 smin(f[node][s1 + s2], f[node][s1] + f[e][s2]); 48 } 49 } 50 clf[node] += clf[e]; 51 } 52 for (int i = 0; i <= clf[node]; i++) 53 smin(f[node][i], f[node][clf[node] - i] + 1); // reverse the color of each node 54 } 55 56 inline void solve() { 57 memset(f, 0x3f, sizeof(f)); 58 treedp(root, 0); 59 int k = clf[root] >> 1; 60 printf("%d\n", f[root][k]); 61 } 62 63 int main() { 64 init(); 65 solve(); 66 return 0; 67 }
Codeforces 581F Zublicanes and Mumocrates - 樹形動態規劃