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子树。
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:\n",++kase); 95 dfs1(1,-1); 96 dfs2(1,-1,0); 97 for(int i=1;i<=n;i++) printf("%d\n",dp[i][1]); 98 } 99 return 0; 100 }