题目链接
http://www.lydsy.com/JudgeOnline/problem.php?id=1509
题目大意
要从一棵树中找出三个点 X,Y,Z ,使得 min(dis[A][C],dis[B][C])+dis[A][B] 最大,求这个最大值
思路
大部分的做法都是基于枚举分叉点的树上DP。这种做法非常好想,但是还是有些难写。
实际上可以发现,min里头的两个东西具体取哪个并不重要,或者说点C距离A更近还是距离B更近并不重要。下面给出一个结论:
min(dis[A][C],dis[B][C])+dis[A][B]
取最大值时,路径
AB
是整个树的直径(最长链),通过BFS找出树的直径后,直接枚举点
C
即可得到最大答案。
关于此题这种做法的具体证明见http://blog.sina.com.cn/s/blog_72aa02bd0100y5vt.html
代码
#include <iostream>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <algorithm>
#include <vector>
#define MAXN 210000
using namespace std;
typedef long long int LL;
struct edge
{
int u,v,w,next;
}edges[MAXN*2];
int head[MAXN],nCount=0;
void AddEdge(int U,int V,int W)
{
edges[++nCount].u=U;
edges[nCount].v=V;
edges[nCount].w=W;
edges[nCount].next=head[U];
head[U]=nCount;
}
int n,m;
bool vis[MAXN];
int q[MAXN];
int BFS(int S,LL dist[]) //求出每个点到S的距离,保存在dist[]数组里,并返回距离S最远的点
{
int farthest=0; //最远点
memset(vis,false,sizeof(vis));
int h=0,t=1;
dist[S]=0; //!!!!
q[h]=S;
vis[S]=true;
while(h<t)
{
int u=q[h++];
for(int p=head[u];p!=-1;p=edges[p].next)
{
int v=edges[p].v;
if(vis[v]) continue;
dist[v]=dist[u]+edges[p].w; //!!!!
if(dist[v]>dist[farthest]) farthest=v;
q[t++]=v;
vis[v]=true;
}
}
return farthest;
}
LL dista[MAXN],distb[MAXN];
int main()
{
memset(head,-1,sizeof(head));
scanf("%d%d",&n,&m);
for(int i=1;i<=m;i++)
{
int u,v,w;
scanf("%d%d%d",&u,&v,&w);
AddEdge(u,v,w);
AddEdge(v,u,w);
}
int a=BFS(1,dista);
int b=BFS(a,dista);
BFS(b,distb);
LL ans=0;
for(int i=1;i<=n;i++)
ans=max(ans,min(dista[i],distb[i]));
printf("%lld\n",ans+dista[b]);
return 0;
}