题目大意:
给出若干棵树,用最少的边把它们连成一张无向连通图,同时使图的直径最小
solution:
求每个树的直径,直径的中心相连,菊花形,把其他树的直径中点连到半径最大的树的直径中点上
根据连接情况讨论计算图的直径
答案有三种:第一种是添加了边之后,树的最长路径还是原来子树的路径,第二种是对子树长度进行排序后,两个最长的距离分别除以2向上取整后加上1。第三种比较难注意到,就是第二第三长的分别除以2向上取整后加上2,因为可能隔着一条边之后比第一种情况更长了。
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#define maxn 100005
using namespace std;
int n,m,cnt,head[maxn],num,d[maxn],t,dis[maxn],b[maxn],ans;
bool vis[maxn];
inline int rd(){
int x=0,f=1; char c=' ';
while(c<'0' || c>'9') {if(c=='-') f=-1;c=getchar();}
while(c<='9' && c>='0') x=x*10+c-'0',c=getchar();
return x*f;
}
struct EDGE{
int to,nxt;
}edge[maxn*2];
void add(int x,int y){
edge[++cnt].to=y;
edge[cnt].nxt=head[x];
head[x]=cnt;
}
void dfs(int x){
vis[x]=1; b[x]=num;
for(int i=head[x];i;i=edge[i].nxt){
int v=edge[i].to;
if(vis[v]) continue;
dis[v]=dis[x]+1;
if(d[num]<dis[v]) d[num]=dis[v],t=v;
dfs(v);
}
return;
}
int main(){
n=rd(); m=rd();
for(int i=1;i<=m;i++){
int x=rd(),y=rd();
add(x,y); add(y,x);
}
for(int i=1;i<=n;i++){
if(!b[i]) {
t=-1; num++;
if(!head[i]) continue;//不加这玩意居然t了???
dfs(i);
memset(dis,0,sizeof dis); memset(vis,0,sizeof vis);
if(t>=0) dfs(t);//这里t要判断一下,如果是单点的话t就是上一个联通块的t了qwq
}
}
sort(d+1,d+num+1);
ans=d[num];
if(num>1) ans=max(ans,(d[num]+1)/2+(d[num-1]+1)/2+1);
if(num>2) ans=max(ans,(d[num-1]+1)/2+(d[num-2]+1)/2+2);
printf("%d\n",ans);
return 0;
}