POJ 3140 Contestants Division 树形DP

题意:

有一棵树,有点权,你可以选择一条边删除,要求删除后形成的2棵子树的权值的差的绝对值最小

输出这个最小值

本来是简单题,一个数组siz

siz[i]表示以i为根的子树的节点的权值之和

然后遍历一遍,找到最小值即可。

注意:

0.点权要long long

1.由于点权之和是long long 的,你在求最小值的时候,初始化ret,

   要初始化为ret=0x3f3f3f3f3f3f3f3f,而不能是0x3f3f3f3f

2.输入的数据描述的是n m

  (1 ≤ N ≤ 100000, 1 ≤ M ≤ 1000000)

  n表示节点的个数,m表示边的个数

  然后我就呆了,题目明明说了这是一棵树啊,也就是边数为n-1啊,这里弄个m<= 1000000是什么?

  然后想想才知道,这是唬人的,数据一定会保证m=n-1的,被骗了,然后还以为自己读错题意了,又看了很久的题目

3.这里的abs()函数要自己写,因为cmath的abs()函数是int abs()的,在这里不适合,会CE

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>

#define ll long long

using namespace std;

const int maxn=1e5+10;
const ll inf=0x3f3f3f3f3f3f3f3f;

struct Edge
{
    int to,next;
};
Edge edge[maxn<<1];
int tot;
int head[maxn];

ll val[maxn];
ll siz[maxn];
ll sum;

void init()
{
    memset(head,-1,sizeof head);
    tot=0;
    sum=0;
}

void addedge(int u,int v)
{
    edge[tot].to=v;
    edge[tot].next=head[u];
    head[u]=tot++;
}

ll solve(int );

int main()
{
    int cas=1;
    int n,m;
    while(scanf("%d %d",&n,&m)){
        if(!n&&!m)
            break;
        printf("Case %d: ",cas++);
        init();
        for(int i=1;i<=n;i++){
            scanf("%lld",&val[i]);
            sum+=val[i];
        }

        for(int i=1;i<=m;i++){
            int u,v;
            scanf("%d %d",&u,&v);
            addedge(u,v);
            addedge(v,u);
        }
        printf("%lld
",solve(n));
    }
    return 0;
}

void dfs(int u,int pre)
{
    siz[u]=val[u];
    for(int i=head[u];~i;i=edge[i].next){
        int v=edge[i].to;
        if(v==pre)
            continue;
        dfs(v,u);
        siz[u]+=siz[v];
    }
}

ll solve(int n)
{
    //printf("eee
");
    dfs(1,-1);

    ll ret=inf;
    for(int i=2;i<=n;i++){
        ll cnt=sum-2*siz[i];
        if(cnt<0)
            cnt*=(-1);
        if(cnt<ret)
            ret=cnt;
    }
    return ret;
}
原文地址:https://www.cnblogs.com/-maybe/p/4825885.html