题意
给你一棵树,边有边权,每经过边一次,就得支付过路费c[i],点上面有宝藏,每个点只能拿一次。
问从每个点出发,能够拿到的最大值是多少?
分析
换根法老祖宗,树形dp必做经典。一上午都献给这题了TAT
分两次dfs进行计算
考虑到从一个点出发的最长路径。两种情况
1.去了儿子回来后,从父亲出去,走到终点。
2.去了父亲回来后,从儿子出去,走到终点。
所以需要分别维护这个点从父亲出去回来和不回来的最大价值,以及从儿子出去回来和不回来的最大价值。
设g[u][0/1]表示从儿子出去回来和不回来 f[u][0/1]表示从父亲出去回来和不回来
g可以从叶节点往根算,但是f需要注意的是,f走出去的最大值的那条路可能是u本身,所以需要维护次大值。
我自己看0和1会看晕,于是这样写dp方程会好很多。
g[u][不回]=max(g[u][不回],g[u][不回]+g[v][回]-2*w) --->在v的子树中绕圈圈后,从u出去,此时u出去的结点是初始值 out[u]=u
g[u][不回]=max(g[u][不回],g[u][回]+g[v][不回]-w) --->在u的子树中绕圈圈后,从v出去,此时u出去的子节点是v,out[u]=v
g[u][回]+=max(0,g[v][回]-2*w ) --->在u的每个有正价值的子树中绕圈圈
当g[v][回]<=2*w 从儿子出去回来的最大价值甚至小于单走到儿子的这条边再回来的花费,不如不走,说明g[u][回]里面没包含走v的情况
f[v][回]=max(f[v][回],f[u][回]+g[u][回]-2*w) --->从v到u,然后在u的父亲、儿子(不包含v)中绕圈圈后,回到v
f[v][不回]=max(f[v][不回],f[u][不回]+g[u][回]-w) --->从v到u,在u的儿子(不包含v)中绕圈圈后,从u的父亲绕圈圈并走到终点
当g[v][回]>2*w
f[v][回]=max(f[v][回],f[u][回]+g[u][回]-g[v][回]) --->从v到u,然后在u的父亲、儿子(包含v)中绕圈圈后,回到v
f[v][不回]=max(f[v][不回],f[u][不回]+g[u][回]-g[v][回]+w) --->从v到u,在u的儿子(包含v)中绕圈圈后,从u的父亲绕圈圈并走到终点
有没有发现上面的不回中只有从u的父亲走到终点,那为什么不可以绕完过后从u的儿子走向终点呢?
其实是可以的,但是这时候你就需要考虑v是不是g[u][不回]的走出去的那个子树呢?
如果是,说明v这个点自己到儿子的价值已经算过了,
枚举u的每个儿子不是v的儿子vk,u和vk间的距离为wk
令tmp=tep=val[u],然后用每个vk更新tmp和tep
tmp=max(tmp,tmp+g[vk][回]-2*wk)
tmp=max(tmp,tep+g[vk][不回]-wk)
tep+=max(0,g[vk][回]-2*wk)
有没有发现这和第一次dfs计算方式很相似?请翻上去类比一下。tmp其实是g[u][不回]的次大值,tep是g[u][回]的次大值。
最后,枚举完所有vk后
f[v][不回]=max(f[v][不回],f[u][回]+tmp-w) --->从v到u,在u的父亲中绕圈圈后,从u(不是v)的儿子走到终点
如果不是,还是分g[u][回]有没有走v讨论
否:f[v][不回]=max(f[v][不回],f[u][回]+g[u][不回]-w) --->从v到u,在u的父亲中绕圈圈,从u(不是v)的儿子绕圈圈(不包含v)并走到终点
是:f[v][不回]=max(f[v][不回],f[u][回]+g[u][不回]-g[v][回]+w) --->从v到u,在u的父亲中绕圈圈,从u(不是v)的儿子绕圈圈(包含v)并走到终点
最后每个点的答案就是max(g[x][回]+f[x][不回],g[x][不回]+f[x][回])
代码
#include<bits/stdc++.h> using namespace std; #define N 100010 int t,n,cnt,cas; int out[N],val[N],pre[N],first[N],f[N][2],g[N][2]; struct email { int u,v,w; int nxt; }e[N*4]; inline void add(int u,int v,int w) { e[++cnt].nxt=first[u];first[u]=cnt; e[cnt].u=u;e[cnt].v=v;e[cnt].w=w; } void dfs(int u,int fa) { g[u][0]=g[u][1]=val[u]; out[u]=u;pre[u]=fa; for(int i=first[u];i;i=e[i].nxt) { int v=e[i].v,w=e[i].w; if(v==fa)continue; dfs(v,u); g[u][1]=max(g[u][1],g[u][1]+g[v][0]-2*w); if(g[u][1]<g[u][0]+g[v][1]-w)g[u][1]=g[u][0]+g[v][1]-w,out[u]=v; g[u][0]+=max(0,g[v][0]-2*w); } } void dfs2(int v,int u,int w) { if(g[v][0]<=2*w) { f[v][0]=max(f[v][0],f[u][0]+g[u][0]-2*w); f[v][1]=max(f[v][1],f[u][1]+g[u][0]-w); } else { f[v][0]=max(f[v][0],f[u][0]+g[u][0]-g[v][0]); f[v][1]=max(f[v][1],f[u][1]+g[u][0]-g[v][0]+w); } if(out[u]==v) { int tmp=val[u],tep=val[u]; for(int i=first[u];i;i=e[i].nxt) { int vk=e[i].v,wk=e[i].w; if(vk==pre[u]||vk==v)continue; tmp=max(tmp,tmp+g[vk][0]-2*wk); tmp=max(tmp,tep+g[vk][1]-wk); tep+=max(0,g[vk][0]-2*wk); } f[v][1]=max(f[v][1],tmp+f[u][0]-w); } else { if(g[v][0]<=2*w)f[v][1]=max(f[v][1],f[u][0]+g[u][1]-w); else f[v][1]=max(f[v][1],f[u][0]+g[u][1]-g[v][0]+w); } for(int i=first[v];i;i=e[i].nxt) if(e[i].v!=u)dfs2(e[i].v,v,e[i].w); } inline void init() { cnt=0;cas++; memset(f,0,sizeof(f)); memset(g,0,sizeof(g)); memset(first,0,sizeof(first)); } int main() { scanf("%d",&t); while(t--) { init(); scanf("%d",&n); 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); add(u,v,w);add(v,u,w); } dfs(1,0);dfs2(1,0,0); printf("Case #%d: ",cas); for(int i=1;i<=n;i++) printf("%d ",max(f[i][0]+g[i][1],f[i][1]+g[i][0])); } return 0; }