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跑个最长路即可。这个也可以用费用流,不过要注意是可行流。