两次dfs
时间复杂度(O(n))
先从任意移动p出发,找离他最远的点q,在从q点出发,找离他最远的点w,w到q的距离就是树的直径
#include <iostream>
#include <cstdio>
using namespace std;
const int maxn = 1e6 + 5;
struct Edge{
int to,next,w;
}e[maxn << 1];
int head[maxn],tot;
void add(int u,int v,int w){
e[++tot].to = v;
e[tot].w = w;
e[tot].next = head[u];
head[u] = tot;
}
int q;//最远的点
int maxdis = 0;
void dfs(int u,int fa,int dis){
if(maxdis < dis){
q = u;
maxdis = dis;
}
for(int i = head[u]; i; i = e[i].next){
int v = e[i].to;
if(v != fa)dfs(v,u,dis+e[i].w);
}
}
int main(){
int n,m;
scanf("%d%d",&n,&m);
while(m--){
int u,v,w;
scanf("%d%d%d",&u,&v,&w);
add(u,v,w);add(v,u,w);
}
dfs(1,0,1);
maxdis = 0;
dfs(q,0,1);
printf("%d
",maxdis);
return 0;
}
树形dp
时间复杂度(O(n))
定义
d[x] 以x为根的子树中,与x最远的结点到x的距离
f[x] 经过点x的所有链中最长一条的长度
edge(u,v) 从结点u到结点v的路径长度
- 从叶子结点开始向上合并,对于每一个结点x的d[x],可以通过枚举x的每条边,然后通过转移方程:
(d[x] = max(d[v_{i}] + edge(x,v_{i}),d[x]))
- 然后对于f[x],若设(v_{1},v_{2})分别为从结点x出发的两个子结点结点,我们可以得到,结果x的最长链是由((x,v_{1}))和((x,v_{2}))组成的。所以在求d[x]时,根据状态转移方程:
(f[x] = max(d[v_{1}] + d[v_{2}] + edge(x,v_{1}) + edge(x,v_{2}),f[x]))
但没必要去枚举(v_{i},v_{j})
因为在求d[x]时已经求了一个x到达的最远距离,然后在进行求f[x]时,只需要用(d[x] + d[v] + edge(x,v))即可,如果(d[x])是从(v_{1})出发到达的最长距离,那么(d[v] + edge(x,v))就是从(v_{2})出发到达的最长距离,然后把两个加上即可
#include <iostream>
#include <cstdio>
using namespace std;
const int maxn = 1e5 + 5;
struct Edge{
int to,next,w;
}e[maxn << 1];
int d[maxn],head[maxn],tot;
void add(int u,int v,int w){
e[++tot].to = v;
e[tot].w = w;
e[tot].next = head[u];
head[u] = tot;
}
int ans = 0;
void DP(int now,int fa){
for(int i = head[now]; i; i = e[i].next){
int v = e[i].to;
if(v == fa)continue;
DP(v,now);
ans = max(ans,d[now] + d[v] + e[i].w);
d[now] = max(d[now],d[v] + e[i].w);
}
}
int main(){
int n,m;
scanf("%d%d",&n,&m);
while(m--){
int u,v,w;
scanf("%d%d%d",&u,&v,&w);
add(u,v,w);add(v,u,w);
}
DP(1,0);
printf("%d
",ans);
return 0;
}