先用一次DFS来判断联通分量的个数(在网上看到很多题解用的是并查集来判断,以后学习一下),同时通过这次DFS找到距离最短的一个点
证明之前,先介绍两个概念,一个是直径,树的deepest root到最远叶子的路径我们暂称直径,而deepest root和最远叶子节点则成为端点。 注明关键在于以下两点:
-
从任意一个点dfs得到的最远点都是直径的端点。证明如下:
任意选一点A,做dfs查找距离A最远的点,因为是dfs,所以肯定会经过某直径上的点B,从B出发最远的点是直径上的端点C。 那么,这个端点C为何一定是A的最远居里点呢?可以反证证明,如果有一个点D,A->D不经过直径,且length(A->D) > length(A->C), 那么,从C所在直径上构造出经过D的比原直径更长的直径,得证。
-
所有的树的直径都交于一点(或者公用段路径上的几点)。 于是,从B点dfs出发的所有最长距离的点即为deepest root。证明如下:
如果两条直径不相交,而树上任意两点肯定连通,则从两直径上,可以选取两点连通以组成更长的直径;如果三条直径相较于不同的两点,也可以根据相交截断的长度组合出更长的直径。
#include<stdio.h>
#include<stdlib.h>
int graph[10000][10000];
int longest[10000],length,dp;
int d[10000];
void deep(int n,int u,int height){
int i,j;
int flag = 0;
for(i = 0;i<n;i++)
if(i!=u && graph[u][i] && !d[i]){
d[i] = 1;
deep(n,i,height+1);
flag = 1;
}
if(flag == 0)
if(height == dp)
longest[length++] = u;
else if(height>dp){
dp = height;
longest[0] = u;
length = 1;
}
}
int DFS(int n,int u){
int i,j;
for(i = 0;i<n;i++)
d[i] = 0;
int NumOfComp = 0;
d[u] = 1;
//dp = 0;
deep(n,u,0);
NumOfComp++;
for(i = (u+1)%n;i!=u;++i,i = i%n)
if(!d[i]){
NumOfComp++;
d[i] = 1;
deep(n,i,0);
}
return NumOfComp;
}
int cmp(const void*a,const void*b)
{
return*(int*)a-*(int*)b;
}
int main(){
int i,j;
int n,NumOfComp = 0;
freopen("1.in","r",stdin);
scanf("%d",&n);
for(int k = 0;k<n-1;k++){
scanf("%d%d",&i,&j);
i--;
j--;
graph[i][j] = graph[j][i] = 1;
}
NumOfComp = DFS(n,0);
if(NumOfComp>1)
printf("Error: %d components\n",NumOfComp);
else{
int sp = longest[0];
DFS(n,sp);
longest[length++] = sp;
DFS(n,longest[0]);
qsort(longest,length,sizeof(int),cmp);
for(i = 0;i<length;i++)
{
if(i&&longest[i] == longest[i-1])
continue;
printf("%d\n",longest[i]+1);
}
}
return 0;}