题目大意:无环连通图可以认为是树(无向),现在给你一个图,有n个结点,n-1条边,让你输出图中结点,输出的结点要满足这种性质:以他为根时树的深度最大,如果这种结点不止一个则按升序依次输出。若这个图不是无环连通图(不能认为是树)时,输出图的连通分量。
思路:先判断是否是无环连通图,因为,已经确定了只有n-1条边,所以如果此图连通则必然没有环,所以先判断是否连通(求连通分量),若连通分量大于1,则直接输出,若连通,则先从任意一个点dfs,可以得到一些点距离这个点最远,再从第一次dfs保存这些点中的任意一个dfs,得到距离它最远的点,两次得到的全部点就是答案。
用并查集求连通分量。因为只有n-1条边所以用链表保存。
#include <cstdio>
#include <cstring>
#include <string>
#include <vector>
#include <set>
#include <malloc.h>
#include <algorithm>
using namespace std;
vector<int> con[10005];
vector<int> v ;//最长根
int maxd=-1;//记录最深深度
int pre[10005];
int *vis;
int find(int x){//找到根源
int r;
int i=x;
while (pre[i]!=i){
i=pre[i];
}
r=i;//找到老大了
int j;
while(x!=r){//把x及x到老大之间的人都弄成老大的直接下属,方便以后找老大
j=pre[x];
pre[x]=r;
x=j;
}
return r;
}
void dfs(int u,int dep){
vis[u]=1;
int count=0;//用来计算出度
for(int n=0;n<con[u].size();n++){
if(vis[con[u][n]]==0){
count++;
dfs(con[u][n],dep+1);
}
}
if (count==0){//说明已经到最深了
if(dep>maxd){
maxd=dep;
v.resize(1);
v[0]=u;
} else if(dep==maxd){
v.push_back(u);
}
}
return ;
}
int main(){
int c,n,k;
scanf ("%d",&n);
c=n;//连通块个数
vector<int> ans;
for(int i=1;i<=n;i++){//使每个节点的老大都为他们自己
pre[i]=i;
}
for (int i=1;i<=n-1;i++){
int x,y;
int f1,f2;
scanf("%d %d",&x,&y);
f1=find(x);
f2=find(y);//f1,f2分别是他们现在的老大
if(f1!=f2){//若属于不同老大(不在一个连通分量里)
pre[f2]=f1;//让f2跟了f1(同时原本f2的下属都属于f1了)
c--;//两家并成了一家,连通分量少了一个
}
con[x].push_back(y);
con[y].push_back(x);//x和y的关系也保存下来
}
if (c>1){
printf ("Error: %d components",c);
return 0;
}else {
vis=(int *)malloc(sizeof(int)*(n+5));//访问标记
for(int i=1;i<=n;i++){
vis[i]=0;//都标为未访问
}
maxd=-1;
dfs(1,0);
for (int i=0;i<v.size();i++){//把第一次dfs之后得到的root都放好
ans.push_back(v[i]);
}
for(int i=1;i<=n;i++){
vis[i]=0;//都标为未访问
}
maxd=-1;v.resize(0);
dfs(ans[0],0);
for(int i=0;i<v.size();i++){
ans.push_back(v[i]);
}
}
sort(ans.begin(),ans.end());
int num=unique(ans.begin(),ans.end())-ans.begin();
for(int i=0;i<num;i++){
printf ("%d\n",ans[i]);
}
return 0;
}