题意:

给出一些树组成的森林,让你添加几条边让它们连成一棵树,并且使树的直径最小。

思路:

要使直径最小,可以想到以前一道题目,可以连成菊花形,所以把直径最大的放在最中间,然后其他的连在它的外面,这样子的话,连成一棵树以后,最大直径肯定只跟两棵树有关,当然一棵树的时候就是树的直径,两棵树的时候是两个树的直径+1,,三棵树及以上的时候,最大直径肯定是最长的树的直径+第二长的树的直径+1或者第二长的树的直径+第三长树的直径+2。

所以先dfs处理出树的直径(先从一棵树的一个点开始找到一个最远的点,再从这个最远的点dfs回来找到的最长的路就是树的直径),然后把这些树的直径从大到小排序再比较一下求一下最大值就好了。

代码:

typedef long long ll;
vector<int>v[100010];
bool vis1[100010];
bool vis2[100010];
int length[100010];
int temp;
int pos;
int ss;
void dfs1(int id,int step)
{
    vis1[id]=true;
    int sign=0;
    for(int i=0;i<v[id].size();i++)
    {
        if(vis1[v[id][i]])continue;
        sign++;
        dfs1(v[id][i],step+1);
    }
    if(sign==0&&step>temp)
    {
        temp=step;
        pos=id;
    }
    return;
}
void dfs2(int id,int step)
{
    vis2[id]=true;
    for(int i=0;i<v[id].size();i++)
    {
        if(vis2[v[id][i]])continue;
        dfs2(v[id][i],step+1);
    }
    ss=max(ss,step);
    return;
}
bool cmp(int x,int y)
{
    return x>y;
}
int main()
{
    int n,m,x,y;
    scanf("%d%d",&n,&m);
    while(m--)
    {
        scanf("%d%d",&x,&y);
        v[x].push_back(y);
        v[y].push_back(x);
    }
    int num=0;
    int ans=0;
    for(int i=0;i<n;i++)
    {
        if(!vis2[i])
        {
            temp=-1;ss=-1;
            dfs1(i,0);
            dfs2(pos,0);
            length[++num]=ss;
            ans=max(ans,ss);
        }
    }
    sort(length+1,length+num+1,cmp);
    //for(int i=1;i<=num;i++)
        //printf("%d\n",length[i]);
    if(num>=2)ans=max(ans,(int)ceil(length[1]/2.0)+(int)ceil(length[2]/2.0)+1);
    if(num>=3)ans=max(ans,(int)ceil(length[2]/2.0)+(int)ceil(length[3]/2.0)+2);
    printf("%d\n",ans);
    return 0;
}