HDU 5834 Magic boy Bi Luo with his excited tree(树形dp)

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

题意:

一棵树上每个节点有一个价值$Vi$,每个节点只能获得一次,每走一次一条边要花费$Ci$,问从各个节点出发最多能收获多少价值。

思路:

需要考虑子节点和父亲节点两个方面。既然是这样,那就需要两次dfs来解决问题了,$dp[u][0]$表示从u出发最后还是回到u的最大价值和,$dp[u][1]$表示从u出发最后不回到u的最大价值和。

第一次dfs很显然就是计算出每个节点往其子树方向的$dp[u][0]$和$dp[u][1]$值。

第二次dfs就是再去考虑每个节点加上其父节点的情况,这里就需要维护一个最大值和一个次大值了,为什么呢?因为父节点的最大值可能最后停在了v子树,那么v子树到时候再去考虑父节点的时候就不对了,所以此时要将它改成次大值,这样它的最大值最后不会停在v子树。

总而言之,这是一道很经典的树形dp,不过没写出来。。

  1 #include<iostream>
  2 #include<algorithm>
  3 #include<cstring>
  4 #include<cstdio>
  5 #include<vector>
  6 #include<stack>
  7 #include<queue>
  8 #include<cmath>
  9 #include<map>
 10 #include<set>
 11 using namespace std;
 12 typedef long long ll;
 13 typedef pair<int,int> pll;
 14 const int INF = 0x3f3f3f3f;
 15 const int maxn=1e5+5;
 16 
 17 int n;
 18 int val[maxn];
 19 int dp[maxn][2];
 20 vector<pll> G[maxn];
 21 
 22 void dfs1(int u, int fa)  //求出每个顶点往子树方向的最大值,dp[u][0]表示最终回到u,dp[u][1]表示最终不回到u
 23 {
 24     dp[u][0]=dp[u][1]=val[u];
 25     for(int i=0;i<G[u].size();i++)
 26     {
 27         int v=G[u][i].first;
 28         int w=G[u][i].second;
 29         if(v==fa)  continue;
 30         dfs1(v,u);
 31         if(dp[v][0]-2*w>0)  dp[u][0]+=dp[v][0]-2*w;
 32     }
 33     for(int i=0;i<G[u].size();i++)
 34     {
 35         int v=G[u][i].first;
 36         int w=G[u][i].second;
 37         if(v==fa)  continue;
 38         int tmp = dp[u][0]-max(0,dp[v][0]-2*w)+(dp[v][1]-w);
 39         if(dp[u][1]<tmp)  dp[u][1]=tmp;
 40     }
 41 }
 42 
 43 void dfs2(int u, int fa, int cost)  //处理往父亲节点的方向
 44 {
 45     int dir_tree=u;
 46     if(fa!=-1)  if(dp[fa][0]-2*cost>0)  dp[u][0]+=dp[fa][0]-2*cost;
 47     int sub_large=dp[u][1]=dp[u][0];
 48     for(int i=0;i<G[u].size();i++)
 49     {
 50         int v=G[u][i].first;
 51         int w=G[u][i].second;
 52         int tmp=dp[u][0]-max(0,dp[v][0]-2*w)+(dp[v][1]-w);
 53         if(tmp>=dp[u][1])  //计算最大权值和次大权值
 54         {
 55             sub_large=dp[u][1];
 56             dp[u][1]=tmp;
 57             dir_tree=v;
 58         }
 59         else if(tmp>=sub_large)   sub_large=tmp;
 60     }
 61     int pre0=dp[u][0],pre1=dp[u][1];  //出去该父节点对其子节点的影响
 62     for(int i=0;i<G[u].size();i++)
 63     {
 64         int v=G[u][i].first;
 65         int w=G[u][i].second;
 66         if(v==fa)  continue;
 67         int tmp=dp[v][0]-2*w;
 68         if(tmp>0)  dp[u][0]-=tmp;  //子节点v往父节点走时,肯定不会再算v节点的子树
 69         if(dir_tree==v)  dp[u][1]=sub_large; //如果u的最大值最后停留在v子树,则改成次大值
 70         if(tmp>0)  dp[u][1]-=tmp;  //和上面的第2句相同
 71         dfs2(v,u,w);
 72         dp[u][0]=pre0,dp[u][1]=pre1;
 73     }
 74 
 75 }
 76 
 77 int main()
 78 {
 79     //freopen("in.txt","r",stdin);
 80     int T;
 81     int kase=0;
 82     scanf("%d",&T);
 83     while(T--)
 84     {
 85         scanf("%d",&n);
 86         for(int i=1;i<=n;i++)  {G[i].clear();scanf("%d",&val[i]);}
 87         for(int i=1;i<n;i++)
 88         {
 89             int u,v,w;
 90             scanf("%d%d%d",&u,&v,&w);
 91             G[u].push_back(make_pair(v,w));
 92             G[v].push_back(make_pair(u,w));
 93         }
 94         printf("Case #%d:
",++kase);
 95         dfs1(1,-1);
 96         dfs2(1,-1,0);
 97         for(int i=1;i<=n;i++)  printf("%d
",dp[i][1]);
 98     }
 99     return 0;
100 }
原文地址:https://www.cnblogs.com/zyb993963526/p/7442451.html