hdu3140(树形dp)

src : http://poj.org/problem?id=3140

题意:选择一条树边断开,使得分成的两部分的总点权差最小,输出最小值

就直接预处理每一个点及其子树的总点权

枚举一个点和其父亲断开,取个最优值就好了

注意long long

#include <iostream>
#include<stdio.h>
#include<cstdlib>
#include<algorithm>
#include<cmath>
#include<functional>
#include<utility>
#include<string>
#include<string.h>
#include<vector>
#include<iomanip>
#include<stack>
#include<queue>
using namespace std;
#define FOR(i,a,b) for(int i=a;i<=b;i++)
#define Max(a,b) a=max(a,b)
#define Min(a,b) a=min(a,b)
//const int inf=0x3f3f3f;
const long long inf=1e18;
#define siz 100005
int n,m,head[siz],Enum,pre[siz];
long long sum[siz],w[siz],all;
struct{
    int to,ne;
}edge[siz<<1];
void init()
{
    Enum=0;
    all=0;
    memset(head,-1,sizeof(head));
    memset(sum,0,sizeof(sum));
}
void add_edge(int a,int b)
{
    edge[Enum].to=b;
    edge[Enum].ne=head[a];
    head[a]=Enum++;
}
void dfs(int u,int fa)
{
    pre[u]=fa;
    sum[u]=w[u];
    for(int i=head[u];i!=-1;i=edge[i].ne){
        int v=edge[i].to;
        if(v==fa)continue;
        dfs(v,u);
        sum[u]+=sum[v];
    }
}

int main()
{
    std::ios::sync_with_stdio(false);
    int cas=0;
    while(scanf("%d %d",&n,&m)!=EOF&&(n||m)){
        cas++;
        init();
        for(int i=1;i<=n;i++){scanf("%d",&w[i]);all+=w[i];}
        int a,b;
        for(int i=1;i<=m;i++){
            scanf("%d %d",&a,&b);
            add_edge(a,b);add_edge(b,a);
        }
        dfs(1,-1);
        //for(int i=1;i<=n;i++)printf("%I64d ",sum[i]);printf("\n");system("pause");
        long long tmp,mi=inf;
        for(int i=2;i<=n;i++){
            if(sum[i]>all-sum[i])tmp=sum[i]-all+sum[i];
            else tmp=all-sum[i]-sum[i];
            //printf("i==%d :%lld\n",i,tmp);
            if(tmp<mi){mi=tmp;}
        }
        printf("Case %d: %lld\n",cas,mi);
    }

    return 0;
}


/*
7 6
1 2 100 4 100 3 2
1 2
2 7
3 7
4 6
6 2
5 7
*/

 

posted @ 2018-07-14 21:28  WindFreedom  阅读(133)  评论(0)    收藏  举报