zoukankan      html  css  js  c++  java
  • Codeforces1294F-Three Paths on a Tree(两次BFS求树的直径)

    题意:

    给一棵树,找到三个顶点,使三个顶点两两之间路径的并集最大

    思路:

    必定会有一组最优解,使得 a,b是树直径上的端点。

    证明:

    假设某个答案取连接点x。x最远的树到达的点是s,根据树的直径算法,s是树的某个直径a的端点。假设x的最远和第二远的点组成的链是b,b就会和a有一段公共部分。我们取a和b相交部分距离s最远的那个点y。那么取这个链上点y的答案一定比x更优  

    用两次BFS可以求出直径的两个端点,在这个过程中还能顺便求出一个端点到树上每一点的距离。之后再用一次BFS求得另一个端点到树上每一点的距离。

    再枚举第三个顶点c就可以求出这三个顶点了

    最终距离为:[dis(a,b)+dis(b,c)+dis(a,c)]/2

    两次BFS求树的直径与端点:

    那么问题来了,怎么求树的直径的呢?这里提供一种两次BFS求树的直径的方法:

     先任选一个起点BFS找到最长路的终点,再从终点进行BFS,则第二次BFS找到的最长路即为树的直径;
     原理: 设起点为u,第一次BFS找到的终点v一定是树的直径的一个端点
     证明: 1) 如果u 是直径上的点,则v显然是直径的终点(因为如果v不是的话,则必定存在另一个点w使得u到w的距离更长,则于BFS找到了v矛盾)
         2) 如果u不是直径上的点,则u到v必然于树的直径相交(反证),那么交点到v 必然就是直径的后半段了
     所以v一定是直径的一个端点,所以从v进行BFS得到的一定是直径长度

    #include<iostream>
    #include<algorithm>
    #include<cstring>
    #include<vector>
    #include<queue>
     using namespace std;
     const int maxn=2e5+10;
     vector<int> a[maxn];
     int n,vis[maxn],dis1[maxn],dis2[maxn],dis[maxn],pos;
     void bfs(int x)
     {
         memset(vis,0,sizeof(vis));
         memset(dis,0,sizeof(dis));
         pos=x;
         vis[x]=1,dis[x]=0;
         queue<int> q;
         q.push(x);
         while(!q.empty()){
             int u=q.front();q.pop();
             for(int i=0;i<a[u].size();i++){
                 if(!vis[a[u][i]]){
                     vis[a[u][i]]=1;
                     dis[a[u][i]]=dis[u]+1;
                     q.push(a[u][i]);
                     if(dis[a[u][i]]>dis[pos]) pos=a[u][i];
                 }
             }
         }
     }
     int main()
     {
         scanf("%d",&n);
         int u,v;
         for(int i=1;i<n;i++){
             scanf("%d%d",&u,&v);
             a[u].push_back(v);
             a[v].push_back(u);
         }
        int a,b,c;
        bfs(1),a=pos;
        bfs(pos),b=pos;
        for(int i=1;i<=n;i++)    dis1[i]=dis[i];
        bfs(pos);
        for(int i=1;i<=n;i++)    dis2[i]=dis[i];
        c=0;
        for(int i=1;i<=n;i++)
            if(dis1[i]+dis2[i]>dis1[c]+dis2[c]&&i!=a&&i!=b)    c=i;
        int ans=(dis1[b]+dis1[c]+dis2[c])/2;
        cout<<ans<<endl<<a<<" "<<b<<" "<<c;
         return 0;
     }
  • 相关阅读:
    java之SFTP上传下载
    java之FTP上传下载
    JUnit单元测试%MODULE_WORKING_DIR%' does not exist
    MySQL 在线DDL "gh-ost"
    MySQL 主从复制错误1837
    <高性能MySQL> 阅读笔记
    Redis cluster 4.0.9 迁槽不影响读写
    MySQL left join 用法与实例
    Linux 日期 date +%F-%T-%N
    MySQL 使用infobin查找binlog中大事务
  • 原文地址:https://www.cnblogs.com/overrate-wsj/p/12231134.html
Copyright © 2011-2022 走看看