POJ 3140 Contestants Division (删边,简单树形DP)

应该说删点类型的题目比较直观,因为dfs的时候都是从点考虑的多。

这题是要删一条边使得剩余的两个分支 价值差最小。

也是做的第一题删边,一开始没底,但是仔细观察一下还是比较简单的。

发现可以这样理解:删一个点和它父亲之间的这条边(因为父亲只有一个)。想到这里(这也不难想)就好办了,只要想办法记录每个节点 从它子树和父亲各能收获多少价值,它们的差值便是删除它和它父亲这条边的结果

最后遍历一遍比较出最小差值就可以了。

具体第一遍dfs,记录子树有多少节点

第二遍dfs,能从父亲转移多少节点,(这里稍微注意一下 = 父亲的父亲+ 父亲的其它孩子)


【代码】

/* ***********************************************
Author        :angon

************************************************ */
#include <stdio.h>
#include <string.h>
#include <iostream>
#include <algorithm>
#include <stack>
#include <vector>
#include <queue>
#include <set>
#include <map>
#include <string>
#include <cmath>
#include <stdlib.h>
#include <time.h>
using namespace std;
#define showtime fprintf(stderr,"time = %.15f\n",clock() / (double)CLOCKS_PER_SEC)
#define lld %I64d
#define REP(i,k,n) for(int i=k;i<n;i++)
#define REPP(i,k,n) for(int i=k;i<=n;i++)
#define scan(d) scanf("%d",&d)
#define scanl(d) scanf("%I64d",&d)
#define scann(n,m) scanf("%d%d",&n,&m)
#define scannl(n,m) scanf("%I64d%I64d",&n,&m)
#define mst(a,k)  memset(a,k,sizeof(a))
#define LL __int64
#define N 100005
#define mod 1000000007
#define Rabs(a)  ((a)<0?-(a):(a))
#define min(a,b) (a)<(b)?(a):(b)
inline int read(){int s=0;char ch=getchar();for(; ch<'0'||ch>'9'; ch=getchar());for(; ch>='0'&&ch<='9'; ch=getchar())s=s*10+ch-'0';return s;}

struct Edge
{
    int v,next;
}edge[N*2];
int head[N],tot;
void addedge(int u,int v)
{
    edge[tot].v=v; edge[tot].next=head[u]; head[u]=tot++;
}
LL val[N],dp1[N],dp2[N];

void dfs1(int u,int fa)
{
    dp1[u] = val[u];
    for(int i=head[u]; ~i; i=edge[i].next)
    {
        int v = edge[i].v;
        if(v==fa) continue;
        dfs1(v,u);
        dp1[u] += dp1[v];
    }
}

void dfs2(int u,int fa)
{
    for(int i=head[u]; ~i; i=edge[i].next)
    {
        int v = edge[i].v;
        if(v==fa) continue;
        dp2[v] += dp2[u];
        dp2[v] += dp1[u] - dp1[v];
        dfs2(v,u);
    }
}
int main()
{
    //freopen("in.txt","r",stdin);
    //freopen("out.txt","w",stdout);
    int n,m,cas=1;
    while(~scann(n,m) && (n||m))
    {
        REPP(i,1,n) scanl(val[i]);
        mst(head,-1); tot=0;
        REP(i,0,m)
        {
            int u,v; scann(u,v);
            addedge(u,v);
            addedge(v,u);
        }
        mst(dp2,0);
        dfs1(1,-1);
        dfs2(1,-1);
        LL minc = 1e15;
        for(int i=1;i<=n;i++)
            minc = min(minc, Rabs(dp1[i] - dp2[i]));
        printf("Case %d: %I64d\n",cas++,minc);
    }


    return 0;
}


【另】


A完之后看了下题解发现有更巧妙的办法,只需第一遍dfs,然后比较每个节点

ans = min(ans,abs((2 * dp[u] - sum)));  

sum - 2倍的子树总节点,即删点该节点上面那条边后两个分支的差值。







  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值