树的直径| CF#615Div3 F. Three Paths on a Tree

Posted fisherss

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了树的直径| CF#615Div3 F. Three Paths on a Tree相关的知识,希望对你有一定的参考价值。

F. Three Paths on a Tree

思路

两种方法:

1.两次bfs求树的直径,顺便求出一个直径端点到所有点的最短距离;再bfs一次,求另一个直径上的端点到其它所有点的最短距离;之后枚举第三个端点(不等于端点1和端点2),dis(a,b) + dis(b,c) + dis(a,c) 再除以 2 就是最终答案,因为每个路径走了两次所以除以2。

2.dfs求树的直径,记录直径上的所有点。从直径上的所有点去搜索它们到不在直径上的点的最远距离。最后直径+这个最远距离就是答案

代码1

bfs

#include<bits/stdc++.h>
using namespace std;

const int maxn = 2e5+10;
struct edge{
    int v;
    edge(int v){
        this -> v = v;
    }
};
vector<edge> vec[maxn];
int d[maxn],ans,dis1[maxn],dis2[maxn];
bool vis[maxn];
int node; // 记录第一次dfs最远的点
void bfs(int u){
    queue<int> q;
    q.push(u);
    while(!q.empty()){
        int x = q.front();
        vis[x] = 1;
        q.pop();
        for(int i = 0;i < (int)vec[x].size();i++){
            int y = vec[x][i].v;
            if(vis[y]) continue;
            d[y] = d[x] + 1;
            if(d[y] > ans){
                ans = d[y];
                node = y;
            }
            q.push(y);
        }
    }
}
int main(){
    int n;
    scanf("%d",&n);
    for(int i=1;i<=n-1;i++){
        int u,v;
        scanf("%d%d",&u,&v);
        vec[u].push_back(edge(v));
        vec[v].push_back(edge(u));
    }
    memset(vis,0,sizeof(vis));
    ans = 0;
    d[1] = 0;
    bfs(1);
    int s1 = node;
    memset(vis,0,sizeof(vis));
    ans = 0;
    d[node] = 0;
    bfs(node);
    memset(vis,0,sizeof(vis));
    int s2 = node;
    int ans1 = ans;
    for(int i=1;i<=n;i++)    dis1[i]=d[i];
    bfs(node);
    for(int i=1;i<=n;i++)    dis2[i]=d[i];
    int s3 = 0;
    for(int i=1;i<=n;i++){
        if(dis1[i]+dis2[i]>dis1[s3]+dis2[s3] && i!=s1 && i!=s2) 
            s3=i;
    }
    int ans=(dis1[s1]+dis1[s3]+dis2[s3])/2;
    cout<<ans<<endl;
    cout<<s1<<" "<<s2<<" "<<s3;
    return 0;
}

代码2

dfs

#include<bits/stdc++.h>
using namespace std;

const int maxn = 1e5+5;
int vis[maxn],book[maxn];
vector<int> g[maxn]; 
int node = 1;
int path[maxn];
int n,m;
int ans = 0;

void init(){
    for(int i=1;i<=n;i++) {
        path[i] = -1;
        vis[i] = 0;
    }
}

//dfs求树的直径 
void dfs(int x,int dis){
    vis[x] = 1; 
    for(int i=0;i<g[x].size();i++){
        int vv = g[x][i];
        if(!vis[vv]){
            path[vv] = x;
            if(dis + 1 > ans) {
                node = vv;
                ans = dis+1;
            }
            dfs(vv,dis+1);
        }
    }
    vis[x] = 0;
}


int lastDis = 0;
int lastId = 0;

void dfs2(int x,int num){
    if(lastDis < num){
        lastDis = num;
        lastId = x;
    }
    for(int i=0;i<g[x].size();i++){
        int v = g[x][i];
        if(!book[v]){
            book[v] = 1;
            dfs2(v,num+1);
        }
    }
}

int main(){
    cin>>n>>m;
    for(int i=1;i<=m;i++){
        int u,v;
        cin>>u>>v;
        g[u].push_back(v);
        g[v].push_back(u);
    }
    init();
    dfs(1,0);
    int t1 = ans;
    ans = 0;
    int s1 = node;
    init();
    dfs(node,0);
    int s2 = node;
    int id = 1;
    //树的直径上的点 都作标记 
    for(int i=s2;i!=-1;i=path[i]){
        book[i] = 1;
        if(i != s1 && i != s2) 
            id = i; //树呈直线型 选一个点作为第三点 
    }
    int maxDist = ans;
    for(int i=1;i<=n;i++){
        //从标记过的点去搜索 到未标记过的点的距离 
        if(book[i] == 1){
            lastDis = 0;
            dfs2(i,0);
            if(ans < maxDist + lastDis){
                ans = maxDist + lastDis;
                id = lastId;
            }
        }
    }
    cout<<ans<<endl;
    cout<<s1<<" "<<s2<<" "<<id<<endl;
    return 0;
} 
/*
8 7
1 2
2 3
3 4
4 5
4 6
3 7
3 8

4 3
1 2
2 3
3 4
*/

以上是关于树的直径| CF#615Div3 F. Three Paths on a Tree的主要内容,如果未能解决你的问题,请参考以下文章

[树的直径]F. Three Paths on a Tree

思维题(取模)| CF#615Div3 D.MEX maximizing

CF 977 F. Consecutive Subsequence

CF455C Civilization 树的直径

Codeforces 835 F. Roads in the Kingdom

Codeforces1294F. Three Paths on a Tree(两次BFS求树的直径)