题意:
给出一些树组成的森林,让你添加几条边让它们连成一棵树,并且使树的直径最小。
思路:
要使直径最小,可以想到以前一道题目,可以连成菊花形,所以把直径最大的放在最中间,然后其他的连在它的外面,这样子的话,连成一棵树以后,最大直径肯定只跟两棵树有关,当然一棵树的时候就是树的直径,两棵树的时候是两个树的直径+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;
}
本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!