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;

}

你可能感兴趣的:(visio)