• 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;
    }
  • 相关阅读:
    ssm 在不同的数据库中进行切换(开启事物禁用)
    引入xfire-all.jar后导致sping配置异常
    单点登录cas的最简单使用
    win10下cmd命令不能用
    前端json 两个重要的方法
    maven 工程下第三方jar 包的引入和打包 war
    idea 中main 方法不能运行
    从sqlServer 分页查询谈${}和#{}
    can与could区别
    线程同步与异步区别
  • 原文地址:https://www.cnblogs.com/qscqesze/p/5771193.html
Copyright © 2020-2023  润新知