傳送門:Computer
題意:有 n 個點,通過 n-1 條邊連成一棵樹,每條邊有一個權值,求從每個點出發的簡單路徑的最大長度。
思路:樹形dp
首先以 1 爲根建樹,這樣每個點能走的最大路徑有兩種情況:往下沿着子節點走或往上沿着父結點走。假設根節點爲 rt,我們定義:
dp[rt][0] 表示以 rt 爲根的子樹上的節點到 rt 的最長距離。
dp[rt][1] 表示以 rt 爲根的子樹上的節點到 rt 的次長距離。
dp[rt][2] 表示從 rt 往上走能走的最長距離。
我們先通過一次 dfs 求出 dp[rt][0] 與 dp[rt][1]。此時只需要簡單的 dfs 和更新就可以了。
然後我們通過第二次 dfs 求出 dp[rt][2]。我們假設 to 是 rt 的一個子節點,此時我們需要分兩種情況考慮:
dp[rt][0] 經過了節點 to,那麼從 to 往上走的最長的路可能是【連接 rt 和 to 的邊】加上(【rt 往上走的最長的路】或【rt 往下走的次長的路】)。
dp[rt][0] 沒有經過節點 to,那麼從 to 往上走的最長的路可能是【連接 rt 和 to 的邊】加上(【rt 往上走的最長的路】或【rt 往下走的最長的路】)。
即:
dp[to][2]=e[i].w+max(dp[rt][2],dp[rt][1])(dp[rt][0] 經過節點 to)
dp[to][2]=e[i].w+max(dp[rt][2],dp[rt][0])(dp[rt][0] 沒有經過節點 to)
這樣我們求出了一個點【往上走的最大距離】和【往下走的最大距離】,最後我們只需要在這兩者中選出一個較大的作爲答案即可。
AC代碼:
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int maxn=1e4+7;
struct node{
int to,nxt,w;
}e[maxn*2];
int tot,head[maxn],dp[maxn][3],mx[maxn];
void add(int u,int v,int w){
e[tot].nxt=head[u];
e[tot].to=v;
e[tot].w=w;
head[u]=tot++;
return;
}
void add_edge(int u,int v,int w){
add(u,v,w);
add(v,u,w);
return;
}
void dfs1(int rt,int p){
for(int i=head[rt];~i;i=e[i].nxt){
int to=e[i].to;
if(to==p) continue;
dfs1(to,rt);
if(dp[to][0]+e[i].w>dp[rt][0]){
mx[rt]=to;
dp[rt][1]=dp[rt][0];
dp[rt][0]=dp[to][0]+e[i].w;
}else if(dp[to][0]+e[i].w>dp[rt][1]){
dp[rt][1]=dp[to][0]+e[i].w;
}
}
return;
}
void dfs2(int rt,int p){
for(int i=head[rt];~i;i=e[i].nxt){
int to=e[i].to;
if(to==p) continue;
if(to==mx[rt]) dp[to][2]=e[i].w+max(dp[rt][2],dp[rt][1]);
else dp[to][2]=e[i].w+max(dp[rt][2],dp[rt][0]);
dfs2(to,rt);
}
return;
}
int main(void){
int n;while(~scanf("%d",&n)){
tot=0;
for(int i=1;i<=n;i++){
head[i]=-1;
dp[i][0]=dp[i][1]=dp[i][2]=0;
}
for(int u=2;u<=n;u++){
int v,w;scanf("%d%d",&v,&w);
add_edge(u,v,w);
}
dfs1(1,0);
dfs2(1,0);
for(int i=1;i<=n;i++){
printf("%d\n",max(dp[i][0],dp[i][2]));
}
}
return 0;
}