Gym-100781A 【树的直径 + 思维】

来源:互联网 发布:联合办学知乎 编辑:程序博客网 时间:2024/06/05 18:10

传送门
//题意: 给你很多颗树(当然也可能只有一棵树), 问你怎样连接起来可以使得形成的树上最远的距离最小,并输出这个距离.
//思路: 当然是和树的直径有关, 然后可以知道从直径折半的地方连出去是最优的, 先用并查集求出每一棵树的直径. 然后需要讨论几种情况, 一是有一棵树它的直径本来就很长, 那么答案有可能是它, 然后(必须两棵树以上)就是直径第一长的和第二长的, 可能是一种答案, 还有一种就是(必须三棵树以上)以直径最长的树作为中转点, 第二长的和第三长的通过这个相连, 那么答案也可能是这种情况最优, 如果你要问为什么不以第三长的作为中转点的话, 那就是没懂起题意, 要让最远的距离最小, 所以只能这样, 细节请看代码.

AC Code

const int maxn = 1e5+5;int cas=1;int pre[maxn],vis[maxn];int n,m;vector<int>g[maxn];int Find(int x){    return pre[x] == x?x: pre[x] = Find(pre[x]); //不要再写成 == 了!!!}int maxx;int dfs(int u,int fa){    int fir = 0, sec = 0;    for(int i=0;i<g[u].size();i++){        int to = g[u][i];        if(to == fa) continue;        int tmp = dfs(to,u);        if(tmp > fir) sec = fir, fir = tmp;        else if(tmp > sec) sec = tmp;    }    if(fir + sec > maxx) maxx = fir + sec;    return fir + 1;}int d[maxn];bool cmp(int a,int b){    return a > b;}void solve(){    while(~scanf("%d%d",&n,&m)){        for(int i=1;i<=n;i++) g[i].clear(),pre[i] = i;        Fill(vis,0);        for(int i=1;i<=m;i++){            int u,v; scanf("%d%d",&u,&v);            u++; v++;            g[u].push_back(v);            g[v].push_back(u);            u = Find(u); v = Find(v);            if(u != v) pre[u] = v;        }        int idx = 0 ;        for(int i=1;i<=n;i++){ //并查集求每棵树的直径.            if(vis[Find(i)]) continue;            vis[Find(i)] = 1;            maxx = 0; dfs(i,-1);            d[idx++] = maxx;        }        sort(d,d+idx,cmp);        int res = d[0]; //第一种情况        if(idx > 1){    //第二种情况            res = max(res,(d[0] + 1)/2 + (d[1] + 1)/2 + 1);        }        if(idx > 2){    //第三种情况            res = max(res,(d[1] + 1)/2 + (d[2] + 1)/2 + 2);        }        printf("%d\n",res);    }}
原创粉丝点击