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
", ++K, mi);
		// for (int i = 1; i <= n; i++) {
		// 	cout << sum[i] << ' ';
		// }
		// cout << endl;
	}
	return 0;
}
原文地址:https://www.cnblogs.com/s1124yy/p/7298585.html