zoukankan      html  css  js  c++  java
  • HDU 5834 Magic boy Bi Luo with his excited tree 树形DP

    Magic boy Bi Luo with his excited tree

    题目连接:

    http://acm.hdu.edu.cn/showproblem.php?pid=5834

    Description

    Bi Luo is a magic boy, he also has a migic tree, the tree has N nodes , in each node , there is a treasure, it's value is V[i], and for each edge, there is a cost C[i], which means every time you pass the edge i , you need to pay C[i].

    You may attention that every V[i] can be taken only once, but for some C[i] , you may cost severial times.

    Now, Bi Luo define ans[i] as the most value can Bi Luo gets if Bi Luo starts at node i.

    Bi Luo is also an excited boy, now he wants to know every ans[i], can you help him?

    Input

    First line is a positive integer T(T≤104) , represents there are T test cases.

    Four each test:

    The first line contain an integer N(N≤105).

    The next line contains N integers V[i], which means the treasure’s value of node i(1≤V[i]≤104).

    Output

    For the i-th test case , first output Case #i: in a single line , then output N lines , for the i-th line , output ans[i] in a single line.

    Sample Input

    1
    5
    4 1 7 7 7
    1 2 6
    1 3 1
    2 4 8
    3 5 2

    Sample Output

    Case #1:
    15
    10
    14
    9
    15

    Hint

    题意

    给你一棵树,边有边权,每经过边一次,就得支付过路费c[i],点上面有宝藏,每个点只能拿一次。

    问从每个点出发,能够拿到的最大值是多少?

    题解:

    显然的树形DP

    dfs两次,第一次dfs维护从这个点的子树出去,再回来能够取得的最大值是多少,不回来的最大值是多少。

    然后第二次dfs,再加上从fa那儿转移过来的值就好了,同样维护回来,和不回来。

    显然答案就是max(从fa回来+从子树不回来,从fa不回来+从子树回来)

    树形DP维护一下这些玩意儿就好了。

    代码

    // writted by dnvtmf
    #include <bits/stdc++.h>
    #define INF 1000000007
    #define FI first
    #define SE second
    #define PB emplace_back
    #define VI vector<int>
    using namespace std;
    typedef long long LL;
    typedef pair <int, int> P;
    const int NUM = 100010;
    struct edge {int next, to, cost;} e[NUM * 2];
    int head[NUM], tot;
    void gInit() {memset(head, -1, sizeof(head)); tot = 0;}
    void add_edge(int u, int v, int c)
    {
        e[tot] = (edge) {head[u], v, c};
    	head[u] = tot++;
    }
    int n, V[NUM], w1[NUM], w2[NUM], id[NUM], ans[NUM];
    //w1是从该点出发最后回到该点的最大值
    //w2是从该点出发有一次不回来的最大值
    //id是不回来的路的子树根节点
    void dfs1(int u, int fa)
    {
    	w1[u] = V[u];
    	w2[u] = V[u];
    	id[u] = -1;
    	for(int i = head[u]; ~i; i = e[i].next) {
    		int v = e[i].to;
    		if(v == fa) continue;
    		dfs1(v, u);
    		int tmp = max(0, w1[v] - e[i].cost - e[i].cost);
    		int tmmp = w1[u] + max(0, w2[v] - e[i].cost);
    		w2[u] += tmp;
    		if(w2[u] < tmmp) {
    			w2[u] = tmmp;
    			id[u] = v;
    		}
    		w1[u] += tmp;
    	}
    }
    //sum1同w1,sum2同w2
    void dfs2(int u, int fa, int sum1, int sum2)
    {
    	ans[u] = max(w1[u] + sum2, w2[u] + sum1);
    	int W1 = w1[u];
    	int W2 = w2[u];
    	int Id = id[u];
    	W2 += sum1;
    	if(W2 <= W1 + sum2) {
    		W2 = W1 + sum2;
    		Id = fa;
    	}
    	W1 += sum1;
    	for(int i = head[u]; ~i; i = e[i].next) {
    		int v = e[i].to;
    		if(v == fa) continue;
    		if(v == Id) {
    			int tmp1 = sum1 + V[u], tmp2 = sum2 + V[u];
    			for(int j = head[u]; ~j; j = e[j].next) {
    				int vv = e[j].to;
    				if(vv == fa || v == vv) continue;
    				int tmp = max(0, w1[vv] - e[j].cost - e[j].cost);
    				tmp2 = max(tmp1 + max(0, w2[vv] - e[j].cost), tmp2 + tmp);
    				tmp1 += tmp;
    			}
    			tmp1 = max(0, tmp1 - e[i].cost - e[i].cost);
    			tmp2 = max(0, tmp2 - e[i].cost);
    			dfs2(v, u, tmp1, tmp2);
    		}
    		else {
    			int tmp = max(0, w1[v] - e[i].cost - e[i].cost);
    			int tmp1 = max(0, W1 - tmp - e[i].cost - e[i].cost);
    			int tmp2 = max(0, W2 - tmp - e[i].cost);
    			dfs2(v, u, tmp1, tmp2);
    		}
    	}
    }
    int main()
    {
    	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);
    			add_edge(u, v, c);
    			add_edge(v, u, c);
    		}
    		dfs1(1, -1);
    		dfs2(1, -1, 0, 0);
    		printf("Case #%d:
    ", cas);
    		for(int i = 1; i <= n; ++i)
    			printf("%d
    ", ans[i]);
    	}
    	return 0;
    }
  • 相关阅读:
    [LeetCode] Power of Three 判断3的次方数
    [LeetCode] 322. Coin Change 硬币找零
    [LeetCode] 321. Create Maximum Number 创建最大数
    ITK 3.20.1 VS2010 Configuration 配置
    VTK 5.10.1 VS2010 Configuration 配置
    FLTK 1.3.3 MinGW 4.9.1 Configuration 配置
    FLTK 1.1.10 VS2010 Configuration 配置
    Inheritance, Association, Aggregation, and Composition 类的继承,关联,聚合和组合的区别
    [LeetCode] Bulb Switcher 灯泡开关
    [LeetCode] Maximum Product of Word Lengths 单词长度的最大积
  • 原文地址:https://www.cnblogs.com/qscqesze/p/5771193.html
Copyright © 2011-2022 走看看