---
tags: CSES題解
---
CSES Problem Set — Tree Diameter 題解
===
題目
---
給一顆有$n$個節點的樹。
定義樹的直徑為,樹中任兩點的最大距離,你的任務是找出這棵樹的直徑。
### 輸入
第一行輸入包含一個整數$n$ : 節點的數量。這些節點編號為$1,2,\ldots,n$。
接下來有$n-1$描述邊。每一行包含兩個整數$a$和$b$ : 點$a$和$b$間有一條邊。
### 輸出
輸出一個整數 : 樹的直徑。
想法 1 :兩次DFS
---
從任一點``DFS``後,距離他最遠點必是樹直徑的一端([詳細證明在這](https://codeforces.com/blog/entry/101271)),再從最遠的這個點``DFS``找到另一個最遠的點就是樹的直徑。
***此方法只適用在邊權皆為正的情況下***
此題邊權皆為+1,因此適用
範例程式碼
---
```cpp=
#include<bits/stdc++.h>
#define int long long
using namespace std;
int cnt=1;
vector<int> Head(200500,0);
vector<int> Next(400500);
vector<int> To(400500);
void addEdge(int u,int v){
Next[cnt]=Head[u];
To[cnt]=v;
Head[u]=cnt++;
}
/*************鍊式前向星建圖****************/
int farn;
int maxd=-1;
void dfs(int n,int par,int d){
if(d>maxd){ //更新最遠點
maxd=d;
farn=n;
}
for(int e=Head[n];e;e=Next[e]){
int x=To[e];
if(x!=par){
dfs(x,n,d+1);
}
}
}
signed main(){
int n;
cin>>n;
for(int i=0;i<n-1;i++){
int a,b;
cin>>a>>b;
addEdge(a,b);
addEdge(b,a);
}
dfs(1,0,0); //找到直徑一端
maxd=-1;
dfs(farn,0,0); //從一端找到直徑
cout<<maxd<<'\n';
return 0;
}
```
想法 2 : 樹形dp
---
如果題目給的樹有負邊權,上述方法就無法正確找出樹的直徑,只能透過其他方法來找直徑,最常見的方法是dp。
我們需要兩個狀態陣列``dist``和``ans``:前者表示一點**開始往下走**的最大距離,後者表示以此點為根節點的子樹的最大距離。
對於一點$x$,先設定``dist[x]``和``ans[x]``為$0$,遞迴完一顆子樹$u$時,**先更新**``ans[x]=max(ans[x],ans[v],dist[x]+dist[u]+x到u的邊權)``
(dist[x]是另一條往下路徑的最大距離,由該路徑通過$x$點再由$u$往下最大距離和原先ans[x]、子節點ans[u]比較取大),
再更新 ``dist[x]=max(dist[x],dist[u]+x到u的邊權)``,
最後答案就會是根節點的ans。
***ans和dist的更新順序很重要***
相反的話可能會計算到重覆路徑。
範例程式碼
---
```cpp=
#include<bits/stdc++.h>
#define int long long
using namespace std;
int cnt=1;
vector<int> Head(200500,0);
vector<int> Next(400500);
vector<int> To(400500);
vector<int> dist(200500,0);
vector<int> ans(200500,0);
void addEdge(int u,int v){
Next[cnt]=Head[u];
To[cnt]=v;
Head[u]=cnt++;
}
/*************鍊式前向星建圖****************/
void dfs(int n,int par){
for(int e=Head[n];e;e=Next[e]){
int v=To[e];
if(v!=par){
dfs(v,n);
//先更新ans
ans[n]=max({ans[v],ans[n],dist[n]+dist[v]+1});
//再更新dist
dist[n]=max(dist[n],dist[v]+1);
}
}
}
signed main(){
int n;
cin>>n;
for(int i=0;i<n-1;i++){
int a,b;
cin>>a>>b;
addEdge(a,b);
addEdge(b,a);
}
dfs(1,1);
cout<<ans[1]<<'\n';
return 0;
}
```