【树形DP】POJ 3140 Contestants Division

通道:http://poj.org/problem?id=3140

题意:一棵n个结点的带权无根树,从中删去一条边,使得剩下来的两棵子树的节点权值之和的绝对值最小,并求出得到的最小绝对值

代码:

#include<iostream>
#include <cstdio>
#include <cstring>
#define min(a,b) ((a)<(b)?(a):(b))
using namespace std;
const int MAX = 100005;
const int EMAX = 1000005;
 
struct{
    int v, nxt;
}edge[2*EMAX];
int k, edgeHead[MAX];
int n, m, val[MAX];
bool vis[MAX];
__int64 ans, sum, sumVal[MAX];
 
void addEdge(int u, int v){
    edge[k].v = v;
    edge[k].nxt = edgeHead[u];
    edgeHead[u] = k ++;
}
 
void calSum(int u){       //  sumVal[u] 记录以u为根的子树的权值之和。
    vis[u] = true;
    sumVal[u] = val[u];
    for(int i = edgeHead[u]; i; i = edge[i].nxt){
        int v = edge[i].v;
        if(vis[v]) continue;
        calSum(v);
        sumVal[u] += sumVal[v];
    }
}
 
__int64 myabs(__int64 val){
    if(val > 0) return val;
    else return -val;
}
 
void dfs(int u){
    vis[u] = true;
    for(int i = edgeHead[u]; i; i = edge[i].nxt){
        int v = edge[i].v;
        if(vis[v]) continue;
        dfs(v);
        ans = min(ans, myabs(sum-sumVal[v]-sumVal[v]));
    }
}
 
int main(){
    int u, v, i, t = 1;
    while(scanf("%d%d", &n, &m) != EOF){
        if(n == 0 && m == 0) break;
        sum = 0, k = 1;
        for(i = 1; i <= n; i ++){
            scanf("%d", &val[i]);
            sum += val[i];
            edgeHead[i] = 0;
        }
        while(m --){
            scanf("%d%d", &u, &v);
            addEdge(u, v);
            addEdge(v, u);
        }
        memset(vis, 0, sizeof(vis));
        calSum(1);
        ans = 999999999999999;        //  ans初始化要足够大,WA了一次。
        memset(vis, 0, sizeof(vis));
        dfs(1);
        if(n == 1) ans = val[1];      //  n=1时要特殊考虑。
        printf("Case %d: %I64d\n", t ++, ans);
    }
    return 0;
}
View Code

 

posted @ 2014-11-03 19:59  mithrilhan  阅读(162)  评论(0编辑  收藏  举报