hdu 5834 Magic boy Bi Luo with his excited tree 树形dp

题目大意:给你一棵树,每个点都有权值,每条边也有权值,从一个点出发,获得点值,走过一条边付出边值,点值只能获得一次,边值走一次付出一次。问从每个点出发,可以获得的最大价值。

每个点记录从这个点出发又回来的最大值re[x],以及从这个点出发最终往一个方向走的最大值ans[x][0]。第一次dfs还要记录下最优值ans[x][0]、次优值ans[x][1],以及最优值是往那条边走的f[x][0]。第二次dfs就是转移过程,从根节点x到u,对根来说,已经处理好,对u来说第一次dfs是没有往x方向走的,所以第二次dfs要处理掉,既往x方向走又回来取得的值加到原有答案上,还有就是最终走向x方向所取得的值,这里就要用到记录下的最优值ans[x][0]、次优值ans[x][1]。

如果x取得的最大值是走向u方向,那么u走向x就要往x次优值走,如果x取得的最大值不是走向u方向,那么u走向x就要往x最优值走。这样就可以处理出答案了。

#include <iostream>
#include <cstdlib>
#include <cstdio>
#include <algorithm>
#include <map>
#include <set>
#include <cstring>
#include <vector>
#define make_pair mk

using namespace std;
const int maxn=100000+11111;
int n;
int v[maxn],re[maxn];
int ans[maxn][2];
int f[maxn][2];
struct edge{
    int u,w,next;
}e[maxn*2];
int head[maxn];
int cnt;

void add(int u,int v,int w){
    e[cnt]=edge{v,w,head[u]};
    head[u]=cnt++;
}
inline void read(int &z)  {
    bool sig=false;
    char ch = getchar();
    while (ch<'0' || ch>'9') {ch=='-'?sig=true:false;ch = getchar();}
    int x = ch - '0';
    while ((ch = getchar()) >= '0'&&ch <= '9') x = x * 10 + ch - '0';
    z= sig?-x:x;
}
inline void out(int x){
    if(x>9)out(x/10);
    putchar(x%10+'0');
}
inline int cal(int x){return x>0?x:0;}

int dfs(int x,int fa){
    re[x]=v[x];
    for(int i=head[x];i!=-1;i=e[i].next){
        int u=e[i].u,w=e[i].w;
        if(u==fa)continue;
        dfs(u,x);
        re[x]+=cal(re[u]-2*w);
    }
    ans[x][0]=re[x];ans[x][1]=re[x];int f1=-1,f2=-1;
    for(int i=head[x];i!=-1;i=e[i].next){
       int u=e[i].u,w=e[i].w;
       if(u==fa)continue;
       int tmp=re[x]-cal(re[u]-2*w)+cal(ans[u][0]-w);
       if(tmp>=ans[x][0]){
        ans[x][1]=ans[x][0];
        ans[x][0]=tmp;
        f2=f1;f1=u;
       }
       else if(tmp>=ans[x][1]){
        ans[x][1]=tmp;
        f2=u;
       }
    }
    f[x][0]=f1;f[x][1]=f2;
}

int dfs2(int x,int fa){
    for(int i=head[x];i!=-1;i=e[i].next){
        int u=e[i].u,w=e[i].w;
        if(u==fa)continue;
        int zz=cal(re[x]-cal(re[u]-2*w)-2*w);
        ans[u][0]+=zz;ans[u][1]+=zz;
        int haha=0;
        if(f[x][0]==u){
            haha=cal(ans[x][1]-cal(re[u]-2*w)-w);
        }
        else {
            haha=cal(ans[x][0]-cal(re[u]-2*w)-w);
        }
        int tmp=re[u]+haha;
        if(tmp>=ans[u][0]){
            ans[u][1]=ans[u][0];
            ans[u][0]=tmp;
            f[u][1]=f[u][0];
            f[u][0]=x;
        }
        else if(tmp>=ans[u][1]){
            ans[u][1]=tmp;
            f[u][1]=x;
        }
        re[u]+=zz;
        dfs2(u,x);
    }
}


int main()
{
    int t;scanf("%d",&t);
    for(int cas=1;cas<=t;cas++){
        cnt=1;
        scanf("%d",&n);
        fill(head,head+n+4,-1);
        for(int i=1;i<=n;i++)read(v[i]);
        for(int i=1;i<n;i++){
            int x,y,w;read(x),read(y),read(w);
            add(x,y,w);
            add(y,x,w);
        }
        dfs(1,-1);
        dfs2(1,-1);
        printf("Case #%d:\n",cas);
        for(int i=1;i<=n;i++){
            out(ans[i][0]);putchar('\n');
        }
    }
    return 0;
}





评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值