F. Three Paths on a Tree
题意
给一个树,让在树上找三个结点A,B,C,使得ABC三个结点3条路径中出现的结点数最多
分析
因为这是一棵树,三条路径必定会有一个交点O,所以我们需要让OA+OB+OC的长度尽可能的长,而OA+OB我们可以直接认为是树的直径,OC认为是这条直径上最长的一个分支。
具体做法
这里我用的是两次DFS求树的直径以及经过的路径结点,
第一次DFS:先从任意一个结点开始DFS到最远的距离,保存最远点A
第二次DFS:从A再进行一次DFS到最远的距离B,并且记录B->A的路径(倒着存的)
此时就以及求出了直径了,也就是OA+OB
第一次遍历直径中的结点,在vis[]数组上做访问标记
第二次遍历直接中的结点,对每一个结点进行DFS寻找最远的分支C点(访问过的不再访问)
最后答案ABC都求出来了
DFS遍历次数比较多,但是总共遍历树只会遍历3次 也就是O(3*n)
代码
#include <iostream>
#include <algorithm>
#include <vector>
#include <cstring>
using namespace std;
const int maxn = 1e6+10;
int N,A,B,C,mx;
int h[maxn],e[maxn],ne[maxn],idx;
int last[maxn],vis[maxn];
void add(int a,int b){
e[idx] = b,ne[idx] = h[a],h[a] = idx++;
}
void DFS(int u,int fa = -1,int dis = 0){
if(dis>=mx){
A = u;
mx = dis;
}
for(int i = h[u];i!=-1;i = ne[i]){
int v = e[i];
if(v == fa) continue;
DFS(v,u,dis+1);
}
}
void DFS2(int u,int fa = -1,int dis = 0){
last[u] = fa;
if(dis>=mx) {
B = u,mx = dis;
}
for(int i = h[u];i!=-1;i = ne[i]){
int v = e[i];
if(v == fa) continue;
DFS2(v,u,dis+1);
}
}
void DFS3(int u,int fa = -1,int dis = 0){
if(dis>=mx){
C = u,mx = dis;
}
for(int i = h[u];i!=-1;i = ne[i]){
int v = e[i];
if(v == fa || vis[v]) continue;
DFS3(v,u,dis+1);
}
}
int main(){
cin>>N;
int a,b;
memset(h,-1,sizeof(h));
for(int i = 0;i<N-1;i++){
scanf("%d%d",&a,&b);
add(a,b);
add(b,a);
}
mx = 0;DFS(1);
mx = 0;DFS2(A);
vis[B] = 1;vis[A] = 1;
for(int i = last[B];i!=A;i = last[i]) {
vis[i] = 1;
}
int d = mx;mx = 0;
for(int i = last[B];i!=A;i = last[i]) DFS3(i);
cout<<d+mx<<endl;
cout<<A<<" "<<B<<" "<<C<<endl;
return 0;
} 
京公网安备 11010502036488号