如何求树的直径

学习博客:https://www.cnblogs.com/ywjblog/p/9254997.html

树的直径
给定一棵树,树中每条边都有一个权值,树中两点之间的距离定义为连接两点的路径边权之和。树中最远的两个节点之间的距离被称为树的直径,连接这两点的路径被称为树的最长链。后者通常也可称为直径,即直径是一个 
数值概念,也可代指一条路径
树的直径通常有两种求法,时间复杂度均为O(n)。我们假设树以N个点N-1条边的无向图形式给出,并存储在邻接表中。

第一种是dp求法,在这里我只介绍bfs求法:

两次BFS(DFS)求树的直径
通过两次BFS或者两次DFS也可以求树的直径,并且更容易计算出直径上的具体节点
详细地说,这个做法包含两步:
1.从任意节点出发,通过BFS和DFS对树进行一次遍历,求出与出发点距离最远的节点记为p
2.从节点p出发,通过BFS或DFS再进行一次遍历,求出与p距离最远的节点,记为q。
从p到q的路径就是树的一条直径。因为p一定是直径的一端,否则总能找到一条更长的链,与直径的定义矛盾。显然地脑洞一下即可。p为直径的一端,那么自然的,与p最远的q就是直径的另一端。
在第2步的遍历中,可以记录下来每个点第一次被访问的前驱节点。最后从q递归到p,即可得到直径的具体方案

下面看一道例题:

题目链接:https://ac.nowcoder.com/acm/contest/884/A

链接:https://ac.nowcoder.com/acm/contest/884/A
来源:牛客网

meeting
时间限制:C/C++ 1秒,其他语言2秒
空间限制:C/C++ 524288K,其他语言1048576K
64bit IO Format: %lld

题目描述

A new city has just been built. There're  nnn interesting places numbered by positive numbers from 111 to nnn.
In order to save resources, only exactly  n−1n-1n1 roads are built to connect these nnn interesting places. Each road connects two places and it takes 1 second to travel between the endpoints of any road.
There is one person in each of the places numbered x1,x2…xkx_1,x_2 \ldots x_kx1,x2xk and they've decided to meet at one place to have a meal. They wonder what's the minimal time needed for them to meet in such a place. (The time required is the maximum time for each person to get to that place.)

输入描述:

First line two positive integers,  n,kn,kn,k - the number of places and persons.
For each the following  n−1n-1n1 lines, there're two integers a,ba,ba,b that stand for a road connecting place aaa and bbb. It's guaranteed that these roads connected all nnn places.
On the following line there're  kkk different positive integers x1,x2…xkx_1,x_2 \ldots x_kx1,x2xk separated by spaces. These are the numbers of places the persons are at.

输出描述:

A non-negative integer - the minimal time for persons to meet together.
示例1

输入

复制
4 2
1 2
3 1
3 4
2 4

输出

复制
2

说明

They can meet at place 1 or 3.

备注:

1≤n≤1051 \leq n \leq 10^51n105
题目大意:给你一颗树,树中某些结点上有人,问你这些人在全部在一点会面的最小是时间是多少
思路:显然就是求最远的两个关键点之间的距离了
看代码:
#include<iostream>
#include<stack>
#include<cstdio>
#include<vector>
#include<queue>
#include<cstring>
using namespace std;
const int maxn=1e5+5;
#define lson l,mid,rt<<1
#define rson mid+1,r,rt<<1|1
typedef long long LL;
const LL INF=1e18;
int cnt=0;
int head[maxn],dis[maxn];
int a[maxn];
bool vis[maxn],key[maxn];
queue<int>q;
struct E
{
    int to,next;
}edge[maxn<<1];
void add(int u,int v)
{
//    cout<<"u:"<<u<<" v:"<<v<<endl;
    edge[cnt].to=v;
    edge[cnt].next=head[u];
    head[u]=cnt++;
}
void bfs(int x)
{
//    cout<<"2:"<<vis[2]<<endl;
    vis[x]=true;
    dis[x]=0;
    q.push(x);
    while(!q.empty())
    {
       int u=q.front();q.pop();
       for(int i=head[u];i!=-1;i=edge[i].next)
       {
            int v=edge[i].to;
            if(!vis[v])
            {

                dis[v]=dis[u]+1;
//                cout<<"v:"<<v<<" dis[v]:"<<dis[v]<<endl;
                vis[v]=true;q.push(v);
            }
       }
    }
}
int main()
{
    memset(head,-1,sizeof(head));
    int N,K;
    cin>>N>>K;
    for(int i=1;i<N;i++)
    {
        int u,v;cin>>u>>v;
        add(u,v);add(v,u);
    }
    for(int i=1;i<=K;i++)
    {
        cin>>a[i];key[a[i]]=true;//标记是否为关键点
    }
    bfs(a[1]);//任取一个关键点
//    for(int i=1;i<=N;i++) cout<<dis[i]<<" ";cout<<endl;
    int ma=dis[a[1]],id=a[1];
    for(int i=1;i<=N;i++)//找最远的关键点
    {
        if(key[i]&&dis[i]>ma)
        {
            ma=dis[i];id=i;
        }
    }
//    cout<<"id:"<<id<<endl;
    memset(dis,0,sizeof(dis));
    memset(vis,false,sizeof(vis));
    bfs(id);
//    for(int i=1;i<=N;i++) cout<<dis[i]<<" ";cout<<endl;
    ma=-1;
    for(int i=1;i<=N;i++)
    {
        if(key[i]&&dis[i]>ma)
        {
            ma=dis[i];
        }
    }
    cout<<(ma+1)/2<<endl;
    return 0;
}

 

转载于:https://www.cnblogs.com/caijiaming/p/11257804.html

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值