POJ 3140 Contestants Division(树形DP)

题意:

给定一棵 n 棵节点的树,求删去某条边后两个分支的最小差异值。

思路:

1. 题目给出来的是一张无向图,所以要设置 vis 标记数组按照深度遍历树的方法来解析,得到 dp[u] : u 为根节点的子树学生数量总数。

2. 最后的到的结果为 ans = min(2 * dp[u] - sum),注意会超过 int 表示范围,用 __int64 范围的数来表示。

3. 学习了一种“孩子链”来表示树的方法,类似于处理 hash 表冲突时采用的“开链法”

#include <iostream>
#include <algorithm>
using namespace std;

#define LL long long int

const int MAXN = 100010;
const LL INFS = 0x3fffffff3fffffff;

struct edge {
    int v;
    edge *next;
} *V[MAXN], ES[MAXN * 2] ;

int EC;
LL dp[MAXN], N[MAXN], sum;
bool vis[MAXN];

inline void addedge(int a, int b)
{
    ES[++EC].next = V[a];
    V[a] = ES + EC; V[a]->v = b;
}

void treedp(int u)
{
    dp[u] = N[u];
    vis[u] = true;

    for (edge* e = V[u]; e; e = e->next)
    {
        if (vis[e->v])
            continue;

        treedp(e->v);
        dp[u] += dp[e->v];
    }

}

LL solve(int n)
{
    LL ans = INFS;
    for (int u = 1; u <= n; ++u)
        for (edge* e = V[u]; e; e = e->next)
            ans = min(ans, _abs64(sum - dp[e->v] * 2));
    return ans;
}

int main()
{
    int n, m, cc = 0;
    while (scanf("%d %d", &n, &m) && n && m)
    {
        sum = 0, EC = 0;

        for (int i = 1; i <= n; ++i)
            scanf("%lld", &N[i]), sum += N[i];

        memset(dp, 0, sizeof(dp));
        memset(V, 0, sizeof(V));
        memset(vis, false, sizeof(vis));

        for (int i = 0; i < m; ++i)
        {
            int a, b;
            scanf("%d %d", &a, &b);
            addedge(a, b);
            addedge(b, a);
        }

        treedp(1);
        LL ans = solve(n);

        printf("Case %d: %lld\n", ++cc, ans);
    }
    return 0;
}
原文地址:https://www.cnblogs.com/kedebug/p/2918725.html