POJ 3140 树形DP
POJ 3140
『Link』POJ 3140
『Type』树形DP
✡Problem:
给定一颗n节点的树,每个结点有k个学生;求删除一条边之后分成的两棵子树的学生数差最小,输出差值。
✡Answer:
这只删除了1条边,只有变成了2个树,那就和求树重心的步骤一样的。比如现在这个点是u,它其中1个儿子是v,那么我们可以删掉u-v这条边,得到的绝对值就是abs(sigm-sum[v]-sum[v]) sigm是学生总数,sum[v]是以v为根节点的学生总数,也就是v这个子树的学生总数。
『Complexity』\(O(n)\)
✡Code:
#include <map>
#include <cmath>
#include <cstdio>
#include <vector>
#include <iostream>
#include <set>
#include <queue>
#include <cstring>
#include <algorithm>
using namespace std;
#define cle(a,v) memset(a,(v),sizeof(a))
#define fo(i,a,b) for(int i=(a);i<=(b);i++)
#define fd(i,a,b) for(int i=(a);i>=(b);i--)
#define ll long long
const int maxn = 1e5 + 7;
struct Edge {
int v, next;
} edges[maxn << 1];
int n, m, head[maxn], tot, u, v, K;
ll sum[maxn], mi, sigm;
void addedge(int u, int v) {
edges[tot].v = v;
edges[tot].next = head[u];
head[u] = tot++;
}
void dfs(int u, int fa) {
for (int i = head[u]; ~i; i = edges[i].next) {
int v = edges[i].v;
if (v == fa) continue;
dfs(v, u);
sum[u] += sum[v];
mi = min(mi, llabs(sigm - 2 * sum[v]));
}
}
void init() {
sigm = tot = 0;
cle(head, -1);
mi = 0x7fffffff7fffffff;
}
int main() {
freopen("1.in", "r", stdin);
while (scanf("%d%d", &n, &m), n + m) {
init();
for (int i = 1; i <= n; i++) {
scanf("%lld", &sum[i]);
sigm += sum[i];
}
for (int i = 0; i < m; i++) {
scanf("%d%d", &u, &v);
addedge(u, v);
addedge(v, u);
}
dfs(1, -1);
printf("Case %d: %lld\n", ++K, mi);
// for (int i = 1; i <= n; i++) {
// cout << sum[i] << ' ';
// }
// cout << endl;
}
return 0;
}