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

转载于:https://www.cnblogs.com/s1124yy/p/7298585.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值