【HDU 5834】Magic boy Bi Luo with his excited tree
題意
給你一棵樹,邊有邊權(quán),每經(jīng)過邊一次,就得支付過路費(fèi)c[i],點(diǎn)上面有寶藏,每個(gè)點(diǎn)只能拿一次。
問從每個(gè)點(diǎn)出發(fā),能夠拿到的最大值是多少?
分析
換根法老祖宗,樹形dp必做經(jīng)典。一上午都獻(xiàn)給這題了TAT
分兩次dfs進(jìn)行計(jì)算
考慮到從一個(gè)點(diǎn)出發(fā)的最長(zhǎng)路徑。兩種情況
1.去了兒子回來(lái)后,從父親出去,走到終點(diǎn)。
2.去了父親回來(lái)后,從兒子出去,走到終點(diǎn)。
所以需要分別維護(hù)這個(gè)點(diǎn)從父親出去回來(lái)和不回來(lái)的最大價(jià)值,以及從兒子出去回來(lái)和不回來(lái)的最大價(jià)值。
設(shè)g[u][0/1]表示從兒子出去回來(lái)和不回來(lái) f[u][0/1]表示從父親出去回來(lái)和不回來(lái)
g可以從葉節(jié)點(diǎn)往根算,但是f需要注意的是,f走出去的最大值的那條路可能是u本身,所以需要維護(hù)次大值。
我自己看0和1會(huì)看暈,于是這樣寫dp方程會(huì)好很多。
g[u][不回]=max(g[u][不回],g[u][不回]+g[v][回]-2*w) --->在v的子樹中繞圈圈后,從u出去,此時(shí)u出去的結(jié)點(diǎn)是初始值 out[u]=u
g[u][不回]=max(g[u][不回],g[u][回]+g[v][不回]-w) --->在u的子樹中繞圈圈后,從v出去,此時(shí)u出去的子節(jié)點(diǎn)是v,out[u]=v
g[u][回]+=max(0,g[v][回]-2*w ) --->在u的每個(gè)有正價(jià)值的子樹中繞圈圈
當(dāng)g[v][回]<=2*w 從兒子出去回來(lái)的最大價(jià)值甚至小于單走到兒子的這條邊再回來(lái)的花費(fèi),不如不走,說(shuō)明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的父親繞圈圈并走到終點(diǎn)
當(dāng)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的父親繞圈圈并走到終點(diǎn)
有沒有發(fā)現(xiàn)上面的不回中只有從u的父親走到終點(diǎn),那為什么不可以繞完過后從u的兒子走向終點(diǎn)呢?
其實(shí)是可以的,但是這時(shí)候你就需要考慮v是不是g[u][不回]的走出去的那個(gè)子樹呢?
如果是,說(shuō)明v這個(gè)點(diǎn)自己到兒子的價(jià)值已經(jīng)算過了,
枚舉u的每個(gè)兒子不是v的兒子vk,u和vk間的距離為wk
令tmp=tep=val[u],然后用每個(gè)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)
有沒有發(fā)現(xiàn)這和第一次dfs計(jì)算方式很相似?請(qǐng)翻上去類比一下。tmp其實(shí)是g[u][不回]的次大值,tep是g[u][回]的次大值。
最后,枚舉完所有vk后
f[v][不回]=max(f[v][不回],f[u][回]+tmp-w) --->從v到u,在u的父親中繞圈圈后,從u(不是v)的兒子走到終點(diǎn)
如果不是,還是分g[u][回]有沒有走v討論
否:f[v][不回]=max(f[v][不回],f[u][回]+g[u][不回]-w) --->從v到u,在u的父親中繞圈圈,從u(不是v)的兒子繞圈圈(不包含v)并走到終點(diǎn)
是:f[v][不回]=max(f[v][不回],f[u][回]+g[u][不回]-g[v][回]+w) --->從v到u,在u的父親中繞圈圈,從u(不是v)的兒子繞圈圈(包含v)并走到終點(diǎn)
最后每個(gè)點(diǎn)的答案就是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:\n",cas);for(int i=1;i<=n;i++)printf("%d\n",max(f[i][0]+g[i][1],f[i][1]+g[i][0]));}return 0; }?
轉(zhuǎn)載于:https://www.cnblogs.com/NSD-email0820/p/9777993.html
總結(jié)
以上是生活随笔為你收集整理的【HDU 5834】Magic boy Bi Luo with his excited tree的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: SQLServer之创建AFETER D
- 下一篇: NSBundle介绍