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.
Print a single number — the minimum number of roads connecting the squares with demonstrations of different parties.
8
1 4
2 4
3 4
6 5
7 5
8 5
4 5
1
5
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 }
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 }
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】博客园携手 AI 驱动开发工具商 Chat2DB 推出联合终身会员
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 聊一聊 C#异步 任务延续的三种底层玩法
· 敏捷开发:如何高效开每日站会
· 为什么 .NET8线程池 容易引发线程饥饿
· golang自带的死锁检测并非银弹
· 如何做好软件架构师
· 欧阳的2024年终总结,迷茫,重生与失业
· 聊一聊 C#异步 任务延续的三种底层玩法
· 上位机能不能替代PLC呢?
· 2024年终总结:5000 Star,10w 下载量,这是我交出的开源答卷
· .NET Core:架构、特性和优势详解