Magic boy Bi Luo with his excited tree

41 篇文章 0 订阅

这里写图片描述




题意:给出一棵树,给出每个节点的价值以及每条路径的花费,每个节点不论经过多少次只能取一次对应的价值,而每条路径每通过一次都要付出对应的代价。要求输出从每一个节点出发的最大获利。




树形dp。
思路挺显然的,但是关键是对细节的处理。先做一次dfs,预处理出每一个节点往下走不会来(v1)以及回来(v2)的最大收益。然后第二次dfs就是伴随记录每个节点往父亲走不会来(fa_v1)以及回来(fa_v2)的最大收益。那么每个节点的最大收益就是往下走回来+往上走不会来 , 或者往上走回来+往下走不会来的最大值。
主要是fa_v1 , fa_v2不太好求。假设当前是fa节点,要走到son节点。递归下去的fa_v1有两种情况:1、就是走除了son的所有fa的儿子节点然后回来,然后走fa的父亲节点然后不会来;2、就是走fa的父亲节点回来,然后走除了son节点的所有儿子节点回来并选择一条增益最大的儿子路径从从回来变成不会来(就是我程序中的max1, max2的作用)。 fa_v2就挺好算了,就是走fa的父亲以及除了son节点的所有儿子节点并且回来的最大值。



#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <algorithm>
using namespace std;

const int maxn = 100010;
int tar[2*maxn], cost[2*maxn], nextt[2*maxn], last[maxn], tot;
int v[maxn], x, y, z, n, ans[maxn];

void insert(int x, int y, int z) {
    tot++;
    tar[tot] = y;
    cost[tot] = z;
    nextt[tot] = last[x];
    last[x] = tot;
}

struct Node {
    int v1, v2, max1, max2;
    //v1 not return
    //v2 return
}tree[maxn];

void dfs(int x, int fa) {
    tree[x].v1 = tree[x].v2 = v[x];
    tree[x].max1 = tree[x].max2 = 0;
    int k = last[x], temp;
    while (k != 0) {
        if (tar[k] == fa) {
            k = nextt[k]; continue;
        }
        dfs(tar[k], x);
        tree[x].v1 = max(tree[x].v1, max(tree[x].v1+max(0, tree[tar[k]].v2-2*cost[k]), tree[x].v2+max(0, tree[tar[k]].v1-cost[k])));
        tree[x].v2 = max(tree[x].v2, tree[x].v2+max(0, tree[tar[k]].v2-2*cost[k]));
        temp = max(tree[tar[k]].v1-cost[k],0)-max(tree[tar[k]].v2-2*cost[k], 0);
        if (temp > tree[x].max2) tree[x].max2 = temp;
        if (tree[x].max2 > tree[x].max1) swap(tree[x].max1, tree[x].max2);
        k = nextt[k];
    }
}

void dfs2(int x, int fa, int fa_v1, int fa_v2) {
    ans[x] = max(tree[x].v1+fa_v2, tree[x].v2+fa_v1);
    int k = last[x], temp, tempv1, tempv2, add;
    while (k != 0) {
        if (tar[k] == fa) {
            k = nextt[k]; continue;
        }
        /*v1 1*/
        tempv1 = fa_v1+tree[x].v2-max(tree[tar[k]].v2-2*cost[k], 0);
        /*2*/
        if (max(tree[tar[k]].v1-cost[k], 0)-max(tree[tar[k]].v2-2*cost[k], 0) == tree[x].max1) add = tree[x].max2;
        else add = tree[x].max1;
        temp = tree[x].v2-max(tree[tar[k]].v2-2*cost[k], 0)+fa_v2+add;
        tempv1 = max(tempv1, temp);

        /*v2*/
        tempv2 = fa_v2+tree[x].v2-max(tree[tar[k]].v2-2*cost[k], 0);

        dfs2(tar[k], x, max(tempv1-cost[k], 0), max(tempv2-2*cost[k], 0));
        k = nextt[k];
    }
}

int main() {
    freopen("hdu.in","r",stdin);
    int T;
    scanf("%d", &T);
    for (int cases = 1; cases <= T; cases++) {
        scanf("%d", &n);
        for (int i = 1; i <= n; i++) scanf("%d", &v[i]);
        tot = 0;
        memset(last, 0, sizeof(last));
        for (int i = 1; i <= n-1; i++) {
            scanf("%d%d%d", &x, &y, &z);
            insert(x, y, z);
            insert(y, x, z);
        }
        memset(tree, 0, sizeof(tree));
        memset(ans, 0 ,sizeof(ans));
        dfs(1, 0);
        dfs2(1, 0, 0, 0);
        printf("Case #%d:\n", cases);
        for (int i = 1; i <= n; i++) printf("%d\n", ans[i]);
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值