树的直径| 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
*/

原文地址:https://www.cnblogs.com/fisherss/p/12294003.html

时间: 2024-09-29 11:43:58

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

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

F. Three Paths on a Tree Description You are given an unweighted tree with nn vertices. Recall that a tree is a connected undirected graph without cycles. Your task is to choose three distinct vertices a,b,ca,b,c on this tree such that the number of

Codeforces Round #615 (Div. 3) F. Three Paths on a Tree

F. Three Paths on a Tree 原题链接:https://codeforces.com/contest/1294/problem/F 题目大意: 给定一棵树,选出三点,使三点连成的j简单路径最大.简而言之,三个点连成的边的集合大小. 解题思路: 假设任取一点为三点连线的公共点,最长路径就是这个点到其他三个点的三条最长边之和,可知这个点一定在直径上(画图分析假设不在时的最长路径可反证).所以先求出树的直径,在使用$ans =(a b+a c+b c) / 2$遍历可以得到第三个点

树的直径求法与性质(附例题)

树的直径指树上距离最远的两点间的距离,它在树上问题上有许多应用,往往通过树的直径的性质可以将一个高时间复杂度的解法变为线性求解.对于树上两点间距离通常有三种定义,我们根据这三种情况分别讨论一下它的性质 树的直径的求法: 树的直径有两种求法,时间复杂度都是O(n) ①贪心求法: 贪心求直径的方法是任意找一个点为根,dfs整棵树找到距离他最远的点xx,再以这个点x为根求出距离它最远的点y,(x,y)即为直径 ②DP求法: DP求直径的方法是对于每个点记录这个点子树中的最长链及与最长链处于不同子树中的

Codeforces 14D Two Paths 树的直径

题目链接:点击打开链接 题意:给定一棵树 找2条点不重复的路径,使得两路径的长度乘积最大 思路: 1.为了保证点不重复,在图中删去一条边,枚举这条删边 2.这样得到了2个树,在各自的树中找最长链,即树的直径,然后相乘即可 #include<stdio.h> #include<iostream> #include<string.h> #include<set> #include<vector> #include<map> #includ

F - Warm up - hdu 4612(缩点+求树的直径)

题意:有一个无向连通图,现在问添加一条边后最少还有几个桥 分析:先把图缩点,然后重构图为一棵树,求出来树的直径即可,不过注意会有重边,构树的时候注意一下 *********************************************************************** #pragma comment(linker, "/STACK:102400000,102400000")#include<stdio.h>#include<string.h

CodeForces 14D 树的直径 Two Paths

给出一棵树,找出两条不相交即没有公共点的路径,使得两个路径的长度的乘积最大. 思路:枚举树中的边,将该边去掉,分成两棵树,分别求出这两棵树的直径,乘起来维护一个最大值即可. 1 #include <iostream> 2 #include <cstdio> 3 #include <cstring> 4 #include <algorithm> 5 using namespace std; 6 7 const int maxn = 200 + 10; 8 9

树的直径 Codeforces Beta Round #14 (Div. 2) D. Two Paths

tiyi:给你n个节点和n-1条边(无环),求在这个图中找到 两条路径,两路径不相交,求能找的两条路径的长度的乘积最大值: #include <iostream> #include <cstdio> #include <cstring> #include <cstdlib> #include <cmath> #include <vector> #include <queue> #include <stack>

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求得另一个端点到树上每一

poj 1985 Cow Marathon 【树的直径】

题目:poj 1985 Cow Marathon 题意:给出一个树,让你求树的直径. 分析: 树的直径:树上两点之间的最大距离. 我们从任意一点出发,BFS一个最远距离,然后从这个点出发,在BFS一个最远距离,就是树的直径. AC代码: /* POJ:1985 Cow Marathon 2014/10/12/21:18 Yougth*/ #include <cstdio> #include <iostream> #include <algorithm> #include