定义
树上任意两点间最短距离的最大值。
两种做法
dfs(bfs)
算法流程
s t e p 1 : step\space 1: step 1:随便选取一个点 p p p,dfs找到离 p p p最远的点 q q q。
s t e p 2 : step\space 2: step 2:对 q q q进行同样的dfs,树上所有点离 q q q的最短距离则为该树的直径。
时间复杂度: O ( n ) O(n) O(n)
正确性证明
我们假设离 P P P最远的点是 Q Q Q。
1. 1. 1. P P P在直径上,易得树的直径为 P Q PQ PQ
2. P 2.P 2.P不在直径上,我们假设树的直径为 A B AB AB
首先,我们讨论
A
B
,
P
Q
AB,PQ
AB,PQ有交点的情况,我们设交点为
C
C
C
由于距离
P
P
P最远的点为
Q
Q
Q,那么我们可得
C
Q
+
B
C
≤
C
Q
+
P
C
CQ+BC\le CQ+PC
CQ+BC≤CQ+PC
即 B C ≤ P C BC\le PC BC≤PC
那么我们有 A B = A C + B C ≤ A C + P C AB=AC+BC\le AC+PC AB=AC+BC≤AC+PC,与 A B AB AB为直径矛盾,故不成立。
然后是 P Q , A B PQ,AB PQ,AB无交点的情况。
如图所示,我们设
N
N
N为
P
Q
PQ
PQ上一点,
M
M
M为
A
B
AB
AB上一点,且有一条路径从
N
N
N到
M
M
M。
我们用和上文相同的方法,得 P Q ≥ P N + N M + M B PQ\ge PN+NM+MB PQ≥PN+NM+MB,即 N Q ≥ N M + M B NQ\ge NM+MB NQ≥NM+MB
所以 N Q + M N ≥ M B NQ+MN \ge MB NQ+MN≥MB, A B ≤ A Q AB\le AQ AB≤AQ,也与 A B AB AB为树的直径矛盾。
综上所述,我们的算法是正确的。
模板题:poj1985
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
int Read(){
int x=0,f=1;
char ch=getchar();
while(!isdigit(ch)){
if(ch=='-') f=-1;
ch=getchar();
}
while(isdigit(ch)){
x=(x<<3)+(x<<1)+ch-'0';
ch=getchar();
}
return x*f;
}
int first[200005],nxt[200005],to[200005],w[200005],tot=0;
void Add(int x,int y,int z){
nxt[++tot]=first[x];
first[x]=tot;
to[tot]=y;
w[tot]=z;
}
int maxn=-1,pos=0;
void dfs(int u,int fa,int dis){
if(dis>maxn){
maxn=dis;
pos=u;
}
for(int e=first[u];e;e=nxt[e]){
int v=to[e];
if(v==fa) continue;
dfs(v,u,dis+w[e]);
}
}
int main(){
int n=Read(),m=Read();
for(int i=1;i<=m;i++){
int x=Read(),y=Read(),z=Read();
Add(x,y,z);
Add(y,x,z);
}
dfs(1,0,0);
maxn=-1;
dfs(pos,0,0);
cout<<maxn<<endl;
}
DP
算法流程
s t e p 1 : step\space1: step 1:找到每个点子树中的最长链与次长链 f [ i ] 与 g [ i ] f[i]与g[i] f[i]与g[i]
s t e p 2 : step\space 2: step 2:求出每个点 f [ i ] + g [ i ] f[i]+g[i] f[i]+g[i]的最大值
时间复杂度: O ( n ) O(n) O(n)
正确性证明
一棵树的直径必定是一条链,那么我们可以想象将一条链折叠,就变成了这样:
那么我们的思路就很清晰了,枚举每一个点,找到其子树中的最长链和次长链(注意不要有重合部分),然后对于所有点求一个最大值即可。
在 D P DP DP是我们采用一个巧妙的方法,用一个 d d d数组来记录一个最大值,如果我们搜到的链长大于我们的 d d d,那么 d d d就成为了我们的次大值,更新 m a x max max即可。
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
int Read(){
int x=0,f=1;
char ch=getchar();
while(!isdigit(ch)){
if(ch=='-') f=-1;
ch=getchar();
}
while(isdigit(ch)){
x=(x<<3)+(x<<1)+ch-'0';
ch=getchar();
}
return x*f;
}
int first[200005],nxt[200005],to[200005],w[200005],tot=0;
void Add(int x,int y,int z){
nxt[++tot]=first[x];
first[x]=tot;
to[tot]=y;
w[tot]=z;
}
int d[200005],maxn=-1;
void dp(int u,int fa){
for(int e=first[u];e;e=nxt[e]){
int v=to[e];
if(v==fa) continue;
dp(v,u);
maxn=max(maxn,d[u]+d[v]+w[e]);
d[u]=max(d[u],d[v]+w[e]);
}
}
int main(){
int n=Read(),m=Read();
for(int i=1;i<=m;i++){
int x=Read(),y=Read(),z=Read();
Add(x,y,z);
Add(y,x,z);
}
dp(1,0);
cout<<maxn<<endl;
}