在達芬奇時代,有一個流行的兒童遊戲稱爲連珠線。固然,這個遊戲是關於珠子和線的。線是紅色或藍色的,珠子被編號爲 1 到 n。這個遊戲從一個珠子開始,每次會用以下方式添加一個新的珠子:html
Append(w, v):一個新的珠子 $w$ 和一個已經添加的珠子 v 用紅線鏈接起來。ios
Insert(w, u, v):一個新的珠子$w$插入到用紅線連起來的兩個珠子$u,v$之間。具體過程是刪去 u,v 之間紅線,分別用藍線鏈接 u,w 和 w, v。app
每條線都有一個長度。遊戲結束後,你的最終得分爲藍線長度之和。spa
給你連珠線遊戲結束後的遊戲局面,只告訴了你珠子和鏈的鏈接方式以及每條線的長度,沒有告訴你每條線分別是什麼顏色。code
你須要寫一個程序來找出最大可能得分。即,在全部以給出的最終局面結束的連珠線遊戲中找出那個得分最大的,而後輸出最大可能得分。orm
第一行一個正整數 $n$,表示珠子的數量。珠子從 1 到 n 編號。htm
接下來 n - 1 行每行三個整數 $a_{i},b_{i},c_{i}$。保證 $1 \leqslant a_{i},b_{i} \leqslant n$,$1 \leqslant c_{i} \leqslant 10000$。表示 ai 號珠子和 bi 號珠子間連了長度爲 ci 的線。blog
輸出一個整數,表示最大可能得分。遊戲
5 1 2 10 1 3 40 1 4 15 1 5 20
140
【樣例描述1】input
能夠經過以下方式得到 60 分:首先從 3 號珠子開始。
把 5 和 3 連起來。(線長度任意)
在 3 和 5 之間插入 $11$。(線長分別爲 40 和 20)。
把 2 和 1 用長度爲 $10$ 的線連起來。
把 4 和 1 用長度爲 $15$ 的線連起來。
【限制與約定】
第一個子任務共 13 分,知足 $1\leqslant n \leqslant 10$
第二個子任務共 15 分,知足 $1\leqslant n \leqslant 200$
第三個子任務共 29 分,知足 $1\leqslant n \leqslant 10000$
第四個子任務共 43 分,知足 $1\leqslant n \leqslant 200000$
Solution:
爲了方便,咱們把每一個珠子當作一個節點
咱們很是開心的發現,結果確定是一棵邊顏色不一樣的樹
因爲剛開始只有任意的一個節點,就至關於咱們選擇一個節點爲根,
而後進行兩種操做:
1.每次擴展一個節點的兒子,即把一個節點和已經在樹上的一個點連上一條紅邊,且不在這兩個點之間加珠子:
或者擴展一個節點的孫子再擴展這個孫子的爸爸,就是把樹上的一個節點連到另外一個節點上,在把一個節點插到這兩個節點之間,把兩條邊染成藍色,即:
PS:若是出現一個根爲藍線的中點,那麼它不多是第一個出現的珠子
好的,假設以1號點爲根,咱們能夠列出一個DP方程式
f[i][0/1]表示當前節點是否爲中點得到的最大價值
$f[i][0]=\sum\limits_{j}^{j \in son[i]}max(f[j][0],f[j][1]+dis(i,j));$
$f[i][1]=f[i][0]+max(f[j][0]-max(f[j][0],f[j][1]+dis(i,j)));(j \in son[i])$
爲了進行換根DP,咱們設$g[i][j][0/1](j \in son[i])$表示f[i][0/1]不考慮兒子j的貢獻所能得到的最大貢獻
也不難求,即:
$g[i][j][0](j \in son[i])=f[i][0]-max(f[j][0],f[j][1]+dis(i,j))$
$g[i][j][1]$同理,記錄一下最大值和次大值便可
而後咱們須要換根進行DP
對於每個兒子,咱們實際上將它的父親當作它的兒子,爲它提供貢獻便可,這個東西仍是直接看代碼好懂一點的吧
void dp2(int x,int y){ if(!son[x].size()) return; if(son[x].size())rep(i,0,son[x].size()-1){ f[x][0]=g[x][0][i]; f[x][1]=g[x][1][i]; if(fa[x]){ f[x][0]+=max(f[fa[x]][0],f[fa[x]][1]+y); f[x][1]=f[x][0]+max(maxxx[x][i],-max(f[fa[x]][0],f[fa[x]][1]+y)+f[fa[x]][0]+y); } ans=max(ans,f[son[x][i]][0]+max(f[x][0],f[x][1]+len[x][i])); dp2(son[x][i],len[x][i]);//因爲是dfs的進行Dp,因此f[fa[x]][0/1]表示的就是以fa[x]爲根的最大價值,故能夠這樣直接轉移 } }
完結撒花o((>ω< ))o
CODE:
#include<iostream> #include<algorithm> #include<cstring> #include<cstdio> #include<vector> #define rep(i,a,b) for(int i=a;i<=(b);i++) #define int long long #define MAXN 200040 using namespace std; int n,to[MAXN<<1],nxt[MAXN<<1],fir[MAXN],val[MAXN<<1],tot,f[MAXN][2],ans,fa[MAXN]; vector<int> son[MAXN],len[MAXN],g[MAXN][2],maxxx[MAXN]; void ade(int x,int y,int z){ to[++tot]=y; nxt[tot]=fir[x]; fir[x]=tot; val[tot]=z; } void dp1(int x,int y){ fa[x]=y; int maxx1=-888888888,maxx2=-888888888; for(int k=fir[x];k;k=nxt[k]){ if(to[k]==y) continue; son[x].push_back(to[k]); dp1(to[k],x); f[x][0]+=max(f[to[k]][0],f[to[k]][1]+val[k]); len[x].push_back(val[k]); if(maxx1<-max(f[to[k]][0],f[to[k]][1]+val[k])+f[to[k]][0]+val[k]) maxx2=maxx1,maxx1=-max(f[to[k]][0],f[to[k]][1]+val[k])+f[to[k]][0]+val[k]; else if(maxx2<-max(f[to[k]][0],f[to[k]][1]+val[k])+f[to[k]][0]+val[k]) maxx2=-max(f[to[k]][0],f[to[k]][1]+val[k])+f[to[k]][0]+val[k]; } f[x][1]=f[x][0]+maxx1; if(!son[x].size()) return; rep(i,0,son[x].size()-1){ g[x][0].push_back(f[x][0]-max(f[son[x][i]][0],f[son[x][i]][1]+len[x][i])); int tmp=-max(f[son[x][i]][0],f[son[x][i]][1]+len[x][i])+f[son[x][i]][0]+len[x][i]; if(tmp==maxx1) g[x][1].push_back(g[x][0].back()+maxx2),maxxx[x].push_back(maxx2); else g[x][1].push_back(g[x][0].back()+maxx1),maxxx[x].push_back(maxx1); } } void dp2(int x,int y){ if(!son[x].size()) return; if(son[x].size())rep(i,0,son[x].size()-1){ f[x][0]=g[x][0][i]; f[x][1]=g[x][1][i]; if(fa[x]){ f[x][0]+=max(f[fa[x]][0],f[fa[x]][1]+y); f[x][1]=f[x][0]+max(maxxx[x][i],-max(f[fa[x]][0],f[fa[x]][1]+y)+f[fa[x]][0]+y); } ans=max(ans,f[son[x][i]][0]+max(f[x][0],f[x][1]+len[x][i])); dp2(son[x][i],len[x][i]); } } main(){ scanf("%lld",&n); rep(i,1,n-1){ int x,y,z; scanf("%lld%lld%lld",&x,&y,&z); ade(x,y,z); ade(y,x,z); } dp1(1,0); dp2(1,-88888888); cout<<ans; return 0; }