树的直径
通俗的讲就是求树上的最长链的长度
一般有两种求法,各有优势
DP法
我们假设树的根是rt
那么树的直径显然会有两种情况
1.直径的两个端点分别在rt的两个子树内
2.直径在rt的某个子树内
这里就可以用到分治的思想,先以rt为根处理情况1,再递归rt的子树将情况2转变为情况1处理
用
d
p
[
u
]
dp[u]
dp[u]表示从结点u出发向u的子树走能走到的最远距离
设
v
i
v_i
vi为u的子节点
那么有
d
p
[
u
]
=
m
a
x
(
d
p
[
v
i
]
+
d
i
s
(
u
,
v
)
)
dp[u]=max(dp[v_i]+dis(u,v))
dp[u]=max(dp[vi]+dis(u,v))
对于以u为根的子树内,经过结点u的最长链长度
m
x
l
e
n
[
u
]
mxlen[u]
mxlen[u]
有
m
x
l
e
n
[
u
]
=
m
a
x
(
d
p
[
v
i
]
+
d
p
[
v
j
]
+
d
i
s
(
v
i
,
u
)
+
d
i
s
(
u
,
v
j
)
)
mxlen[u]=max(dp[v_i]+dp[v_j]+dis(v_i,u)+dis(u,v_j))
mxlen[u]=max(dp[vi]+dp[vj]+dis(vi,u)+dis(u,vj))
由于dfs的特性,这里我们并不需要
O
(
n
2
)
O(n^2)
O(n2)的枚举
假设当前搜到的
u
u
u的子节点为
v
i
v_i
vi
那么显然
d
p
[
u
]
dp[u]
dp[u]已经被
d
p
[
v
1
]
dp[v_1]
dp[v1]~
d
p
[
v
i
−
1
]
dp[v_{i-1}]
dp[vi−1]中最大的一个更新过
所以这里可以直接用已有的
d
p
[
u
]
dp[u]
dp[u]代替另一个子节点的枚举
最后整个树的最长链就是 m a x ( m x l e n [ u ] ) 1 < = u < = n max(mxlen[u])1<=u<=n max(mxlen[u])1<=u<=n
整个算法只需一次dfs,时间复杂度 O ( n ) O(n) O(n)
void DP(int u,int pa)
{
dp[u]=0;
for(int i=head[u];i;i=E[i].nxt)
{
int v=E[i].v;
if(v==pa) continue;
DP(v,u);
mxlen=max(mxlen,dp[u]+dp[v]+E[i].dis);//这里直接用一个全部变量更新也可以
dp[u]=max(dp[u],dp[v]+E[i].dis);
}
}
DFS || BFS 法
算法总共两次dfs或bfs
第一步
从任意节点出发,通过dfs或bfs遍历找到与出发点最远的结点,记为p
第二步
从p出发,通过dfs或bfs遍历找到离p最远的结点,记为q
此时p到q的路径就是树的直径
该算法整体复杂度也为
O
(
n
)
O(n)
O(n),常数比DP法稍大
但是可以同时得知直径的具体结点,以便于其他操作
void dfs(int u,int pa)
{
//dp[u]记录从u出发能到达的最远距离,rem[u]记录离u最远的结点
rem[u]=u; dp[u]=0;
for(int i=head[u];i;i=E[i].nxt)
{
int v=E[i].v;
if(v==pa) continue;
dfs(v,u);
if(dp[u]<=dp[v]+E[i].dis)
dp[u]=dp[v]+E[i].dis,rem[u]=rem[v];
}
}
dfs1(1,0); p=rem[1];
dfs1(p,0); q=rem[p];
需要特别注意的是
当树中有负边权时不能使用该方法
#include<iostream>
#include<vector>
#include<algorithm>
#include<queue>
#include<cstring>
#include<cstdio>
using namespace std;
int read()
{
int f=1,x=0;
char ss=getchar();
while(ss<'0'||ss>'9'){if(ss=='-')f=-1;ss=getchar();}
while(ss>='0'&&ss<='9'){x=x*10+ss-'0';ss=getchar();}
return f*x;
}
const int maxn=100010;
int n,m;
struct node{int v,dis,nxt;}E[maxn<<2];
int head[maxn],tot;
int dp[maxn],mxlen;
void add(int u,int v,int dis)
{
E[++tot].nxt=head[u];
E[tot].v=v; E[tot].dis=dis;
head[u]=tot;
}
void DP(int u,int pa)
{
dp[u]=0;
for(int i=head[u];i;i=E[i].nxt)
{
int v=E[i].v;
if(v==pa) continue;
DP(v,u);
mxlen=max(mxlen,dp[u]+dp[v]+E[i].dis);
dp[u]=max(dp[u],dp[v]+E[i].dis);
}
}
int main()
{
while(scanf("%d%d",&n,&m)!=EOF)
{
memset(head,0,sizeof(head));
tot=mxlen=0;
while(m--)
{
int u=read(),v=read(),dis=read();
char ss[5]; scanf("%s",&ss);
add(u,v,dis); add(v,u,dis);
}
DP(1,0);
printf("%d",mxlen);
}
return 0;
}