HDU 6201 2017沈阳网络赛 树形DP或者SPFA最长路

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=6201

题意:给出一棵树,每个点有一个权值,代表商品的售价,树上每一条边上也有一个权值,代表从这条边经过所需要的花费。现在需要你在树上选择两个点,一个作为买入商品的点,一个作为卖出商品的点,当然需要考虑从买入点到卖出点经过边的花费。使得收益最大。允许买入点和卖出点重合,即收益最小值为0。

解法:我们设1为根节点,假设一开始一个人身上的钱为0。我们设dp[i][0]表示从根节点走到i及其子树并中任一点买入一本书后这个人身上钱的最大值(显然是负的)。dp[i][1]表示从根节点走到i及其子树并中任一点卖出一本书后这个人身上钱的最大值(可正可负)。那么我们对这棵树进行一次树形DP即可,dfs后对每个节点更新收益最大值,单点的计算方法为dp[i][0]+dp[i][1],树形DP的过程中即可维护这个最大值。

 

#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int maxn = 100010;
struct node{
    int v,w;
};
vector <node> G[maxn];
int val[maxn];
int dp[maxn][2];
int n, ans;
void dfs(int x, int pre){
    dp[x][0] = -val[x];
    dp[x][1] = val[x];
    for(int i=0; i<G[x].size(); i++){
        int v = G[x][i].v;
        int w = G[x][i].w;
        if(v == pre) continue;
        dfs(v, x);
        dp[x][0] = max(dp[x][0], dp[v][0]-w);
        dp[x][1] = max(dp[x][1], dp[v][1]-w);
    }
    ans = max(ans, dp[x][0]+dp[x][1]);
}
int main()
{
    int T;
    scanf("%d", &T);
    while(T--){
        scanf("%d", &n);
        for(int i=0; i<=n; i++) G[i].clear();
        for(int i=1; i<=n; i++) scanf("%d", &val[i]);
        for(int i=1; i<n; i++){
            int u, v, w;
            scanf("%d %d %d", &u,&v,&w);
            G[u].push_back(node{v,w});
            G[v].push_back(node{u,w});
        }
        ans = 0;
        dfs(1,-1);
        printf("%d\n", ans);
    }
    return 0;
}

 

除了DP,还看到一个方法,就是建立源点和汇点。源点连所有的树上点, 边权为 a[i], 所有树上点在连接 汇点, 边权为-a[i]. 然后在根据树建图。 spfa跑个最长路即可。这个也可以用费用流,不过要注意是可行流。


posted @ 2017-09-11 17:41  zxycoder  阅读(230)  评论(0编辑  收藏  举报