树的直径 hdu 4607

10 篇文章 0 订阅
2 篇文章 0 订阅

树的直径定义:树中所有最短路径的最大值。

定义distance(a, b)为顶点a与b之间的最短距离。

用数学语言表示就是:对于一棵树 T = (V, E),彐vx, vy ∈ V,  ∀ v1, v2∈V, 都有distance(v1, v2) < distance(vx, vy). 则把vx到vy的最短路径称为树T的直径。

现在我们来用算法求解树的直径。

求解过程很简单,两次BFS。

(1)第一次BFS我们可以从任意顶点出发,不妨设该顶点为 t, 找到距离 t 最远的顶点u。则u一定为树的直径中的某一个顶点。

(2)第二次BFS我们需要从顶点u出发,这次找到距离u最远的顶点v,则u到v的最短路径就是树的直径。 

证明:假设树的直径为 u - v

①假设 t 是 u - v这条路径上的点,同时我们找到距离 t 最远的点是p, p ≠u 且 p ≠ v。那么, distance(t , p)  > distance(t, v)且distance(t, p) > distance(t, u)。故有distance(t, p)  + distance(t , v)  > distance(u, v) 或 distance(t, p)+ distance(t, u) > distance(u, v)。 这与 u-v是直径矛盾。

② I :如果 t 不是 u - v这条路径上的点,但 t 在找距其最远的点时经过了u - v路径上的某个点,则 回到情况①。

     II:如果 t 不是 u - v这条路径上的点,并且 t 在找距其最远的点时没有经过u - v路径上的点。同样,我们找到距离 t 最远的点是p, p ≠u 且 p ≠ v。

   这个时候,我们设从t 到直径上会经过点 q。

  显然有 distance(t , p)  > distance(q, v)且distance(t, p) > distance(q, u)。不妨设distance(q, u) > distance(q, v).

 同时有 distance(q, p) = distance(q, t) + distance(t, p) > distance(q, v), 所以有 distance(q, p) + distance(q, u) > distance (q, u) + distance(q, v)。

这与u-v是直接相矛盾。

证毕。

hdu4607题意:给定一棵树,每两点间的距离都是 1,从任意一个顶点出发,访问k个点(包括起点),要求走的距离最小。求最小距离。

思路:显然,从直径的顶点出发,如果一直走直径,则每次访问一个点只需要 1 的距离(没有更短的距离了),所以,当 k < 直径时,就直接输出 k - 1.当 k > 直径时,需要访问的点除了直径上的点,还有(k - 直径)个点,那么不管怎么走,都必须回到直径上来。离开直径,回到直径的距离就是(k - 直径)* 2.最后答案别忘了减1.因为起点也算在内。

#include<stdio.h>
#include<string.h>
#include<algorithm>
#include <utility>
#include<vector>
#include <iostream>
#include<queue>

using namespace std;

const int N = 100000+16;

typedef vector<int>::iterator vi;
typedef pair<int, int> pairii;
vector<int> G[N];
queue<int> que;

int lev[N];
int vis[N];

pairii bfs(int s)
{
    que.push(s);
    memset(vis, 0, sizeof(vis));
    lev[s] = 1;
    int maxlev = 1;
    int v = s;
    while(!que.empty())
    {
        int cur = que.front();
        if(lev[cur] > maxlev) maxlev = lev[cur], v = cur;
        que.pop();
        vis[cur] = 1;
        for(vi i = G[cur].begin(); i != G[cur].end(); i++)
        {
            if(!vis[*i])
            {
                lev[*i] = lev[cur] + 1;
                que.push(*i);
            }
        }
    }
    pairii ret = make_pair(v, maxlev);
    return ret;
}


int main()
{
    int t;
    scanf("%d", &t);
    while(t--)
    {
        int n, m;
        scanf("%d%d", &n, &m);
        for(int i = 1; i <= n; i++)G[i].clear();
        for(int i = 1; i < n; i++)
        {
            int a, b;
            scanf("%d%d", &a, &b);
            G[a].push_back(b);
            G[b].push_back(a);
        }
         pairii t1 = bfs(1);
        pairii t2 = bfs(t1.first);
        int diameter = t2.second;
        for(int i = 0; i < m; i++)
        {
            int k;
            scanf("%d", &k);
            if(k <= diameter)
                printf("%d\n", k-1);
            else printf("%d\n", diameter + (k - diameter)*2 - 1);

        }
    }

    return 0;

}



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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值