By pjx,拿走请附上https://www.cnblogs.com/pjxpjx/p/12432463.html
树的直径
零、前言
这是几个月前写了一半的东西好大一口锅,填个坑
*如果没有学过树上前缀和,建议先阅读该文献
一、定义
一些无向边且权值均为正整数构成的树上(可以无根),距离最长的2个点的距离为树的直径。
二、解法(边权为例)
1、思路
先任意从一点(a) 出发,找到离它最远的那个点(b);
再从(b) 出发,找到离(b) 最远的点(c);
则(b) 到(c) 的距离即为树的直径。
下面给出证明:
2、证明
不难得证,因为树上每两点间只可能有(1)条路径,长度都唯一,
分情况讨论:
1、如果(a) 点就为树的直径的起始点,则找到的(b) 点就是终点,再以(b) 为起点,又回到(a) 点;
2、如果(a) 不为树直径的两个端点,则(b) 离(a) 最远。如果(b) 不为直径的端点,那么一定有一个点比(b) 点远,那个点就是直径的端点。(因为为正整数权值,(b) 连向直径的端点那一段路也会让权值变得更大)
综上,(b) 便为直径的一个端点,与(b) 点最远的点就一定是另一个端点了。
得证。
3、树上前缀和 + DFS大法
如何找出最远的点呢?前缀和是个好东西。先跑一边从(a) 开始的前缀和,找到 (b),从(b) 跑前缀和,找到(c), 输出前缀和那个值就行了。
三、code:
#include <iostream>
#include <cstdlib>
#include <cstdio>
#include <algorithm>
#include <cmath>
#include <cstring>
#include <queue>
using namespace std;
const int N = 10005;
int b[N], cnt, n , m, head[N], total[N], total2[N], b2[N];
struct node{
int u, v, w, next;
}ed[N];
void add_edge(int u,int v,int w)//邻接表存图
{
cnt++;
ed[cnt].u=u;
ed[cnt].v=v;
ed[cnt].w=w;
ed[cnt].next=head[u];
head[u]=cnt;
}
void dfs(int xx)//第一次前缀和
{
//前面没有赋自己的值,不要弄混两个前缀和
for(int i=head[xx];i!=0;i=ed[i].next)
{
int temp=ed[i].v;
if(!b[temp])
{
b[temp]=1;
total[temp]=ed[i].w+total[xx];
dfs(temp);
}
}
}
void dfs2(int xx)//这是第二次前缀和
{
for(int i=head[xx];i!=0;i=ed[i].next)
{
int temp=ed[i].v;
if(!b2[temp])
{
b2[temp]=1;
total2[temp]=ed[i].w+total2[xx];
dfs2(temp);
}
}
}
int main()
{
cin>>n;
for(int i=1;i<=n-1;i++)
{
int x,y,k;
cin>>x>>y>>k;
add_edge(x,y,k);
add_edge(y,x,k);
}
b[1]=1;
dfs(1);//第一遍,从a找到b
int root,maxn=-1;
for(int i=1;i<=n;i++)
{
if(total[i]>maxn)//找出最大值
{
maxn=total[i];
root=i;
}
}
b2[root]=1;
dfs2(root);//第二遍,从b再找到c
int root2,maxn2=-1;
for(int i=1;i<=n;i++)
{
if(total2[i]>maxn2)//找出最大值
{
maxn2=total2[i];
root2=i;
}
}
cout<<maxn2;
return 0;
}
/*
Input:
7
1 2 2
1 3 1
5 6 3
4 6 4
3 5 1
5 7 3
Output:
11
*/
样例数据如图所示:
自己程序结果如图: