HDU 5834 树形DP

来源:互联网 发布:找回淘宝账号密码 编辑:程序博客网 时间:2024/05/22 03:02

HDU 5834 树形DP

『题目链接』HDU 5834

『题目类型』树形DP

✡PROBLEM:

给你一棵树,边有边权,每经过边一次,就得支付过路费c[i],点上面有宝藏,每个点只能拿一次。问从每个点出发,能够拿到的最大值是多少?

✡ANSWER:

参考链接:http://blog.csdn.net/angon823/article/details/52266807
dfs两次:第一次dfs维护从这个点的子树出去,再回来能够取得的最大值是多少,不回来的最大值是多少。
然后第二次dfs,再加上从fa那儿转移过来的值就好了,同样维护回来,和不回来。
答案就是max(从fa回来+从子树不回来,从fa不回来+从子树回来)
先dfs一遍处理出:
dp[u][0],最后一次不回来最大;dp[u][1],最后一次不回来次大;dp[u][2] , 最后一次回来的值;(以上都是在子树范围下,所以dp[u][i]是包含了其所有子树信息的)
id[u] ,最后一次不回来的孩子id
无疑:这里最关键的,也难点部分就在于 怎么处理“次大”,有时候我们我们要求“次大”不能和“最大”同样大,或者不能在同一个子树上。但是这里的“次大”却不一样,应该说要根据在第二遍dfs里怎么用这个“次大”来决定它是怎样的。

『时间复杂度』O(n)

✡CODE:

#include <map>#include <cmath>#include <cstdio>#include <vector>#include <iostream>#include <set>#include <queue>#include <cstring>#include <algorithm>using namespace std;#define cle(a,v) memset(a,(v),sizeof(a))#define fo(i,a,b) for(int i=(a);i<=(b);i++)#define fd(i,a,b) for(int i=(a);i>=(b);i--)#define ll long longconst int maxn = 1e5 + 7;int n, head[maxn], tot, V[maxn];int dp[maxn][3], id[maxn], ans[maxn];struct Edge {    int to, cost, next;} edges[maxn << 1];void gInit() {    cle(head, -1); tot = 0;    cle(dp, 0);}void added(int u, int v, int w) {    edges[tot] = Edge{v, w, head[u]};    head[u] = tot++;}void dfs1(int u, int fa) {    dp[u][2] = V[u];    for (int i = head[u]; ~i; i = edges[i].next) {        int v = edges[i].to;        if (v == fa) continue;        dfs1(v, u);        dp[u][2] += max(0, dp[v][2] - 2 * edges[i].cost);    }    id[u] = -1;    dp[u][0] = dp[u][1] = V[u];    for (int i = head[u]; ~i; i = edges[i].next) {        int ad = edges[i].to;        int wa = edges[i].cost;        if (ad == fa) continue;        int now = dp[u][2] - max(0, dp[ad][2] - 2 * wa) + max(0, dp[ad][0] - wa);        if (now >= dp[u][0]) {            dp[u][1] = dp[u][0];            dp[u][0] = now;            id[u] = ad;        }        else if (now > dp[u][1]) dp[u][1] = now;    }    // printf("u = %d %d %d %d\n", u, dp[u][0], dp[u][1], dp[u][2]);}void dfs2(int u, int fa, int up1, int up2) {    ans[u] = max(dp[u][0] + up2, dp[u][2] + up1);    for (int i = head[u]; ~i; i = edges[i].next) {        int v = edges[i].to;        int cost = edges[i].cost;        if (v == fa) continue;        int d1, d2;        if (v == id[u]) {            d1 = max(0, dp[u][1] - max(0, dp[v][2] - 2 * cost));        }        else {            d1 = max(0, dp[u][0] - max(0, dp[v][2] - 2 * cost));        }        d2 = max(0, dp[u][2] - max(0, dp[v][2] - 2 * cost));        d1 = max(0, max(up2 + d1, up1 + d2) - cost);        d2 = max(0, d2 + up2 - 2 * cost);        dfs2(v, u, d1, d2);    }}int main() {    freopen("1.in", "r", stdin);    int T; scanf("%d", &T);    for (int cas = 1; cas <= T; cas++) {        gInit();        scanf("%d", &n);        for (int i = 1; i <= n; i++)            scanf("%d", &V[i]);        for (int i = 1; i < n; i++) {            int u, v, c;            scanf("%d%d%d", &u, &v, &c);            added(u, v, c); added(v, u, c);        }        dfs1(1, -1);        dfs2(1, -1, 0, 0);        printf("Case #%d:\n", cas);        for (int i = 1; i <= n; i++) {            printf("%d\n", ans[i]);        }    }    return 0;}