2019牛客多校第四场A meeting——树的直径

题意:

一颗 $n$ 个节点的树上标有 $k$ 个点,找一点使得到 $k$ 个关键结点的最大距离最小。

分析:

问题等价于求树的直径,最小距离即为直径除2向上取整。

有两种求法,一是动态规划,对于每个结点,把所有子结点的 $d(i)$ (表示根为 $i$ 的子树中根到叶子的最大距离)都求出来,设 $d$ 值前两大的结点为 $u$ 和 $v$,则 $d(u) + d(v) +2$就是树的直径。

另一种是两次DFS,从任意一个关键节点开始,找到离它最远的关键结点 $y$,从 $y$ 出发dfs找到新的最远结点 $z$,形成的就是直径。

代码:

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

const int maxn = 1e5 + 10;

struct Edge
{
    int to, w, next;
}edges[maxn*2];
int head[maxn], cnt;
int n, k;
int vis[maxn], dis[maxn];      //顶点编号
set<int>st;

void init()
{
    memset(head, -1, sizeof(head));
    cnt = 0;
}

inline void AddEdge(int a, int b, int id)
{
    edges[id].to = b;
    edges[id].w = 1;
    edges[id].next = head[a];
    head[a] = id;
}

int dis_v, max_dis;
void dfs(int v, int dis)
{
    //printf("%d %d\n", v, dis);
    vis[v] = 1;
    for(int i = head[v];i != -1;i = edges[i].next)
    {
        int u = edges[i].to;
        if(vis[u])  continue;
        if(st.find(u) != st.end())
        {
            if(dis+1 > max_dis)
            {
                max_dis = dis + 1;
                dis_v = u;
            }
        }
        dfs(u, dis+1);
    }
}

int main()
{
    init();
    scanf("%d%d", &n, &k);
    for(int i =  0;i < n-1;i++)
    {
        int a, b;
        scanf("%d%d", &a, &b);
        AddEdge(a, b, ++cnt);
        AddEdge(b, a, ++cnt);
    }
    for(int i = 0;i < k;i++)
    {
        int tmp;
        scanf("%d", &tmp);
        st.insert(tmp);
    }
    dfs(*(st.begin()), 0);  //printf("%d %d\n", dis_v, max_dis);
    memset(vis, 0, sizeof(vis));
    max_dis = 0;
    dfs(dis_v, 0); //printf("%d %d\n", dis_v, max_dis);
    printf("%d\n", (max_dis+1)/ 2);

    return 0;
}

 

posted @ 2019-07-28 15:55  Rogn  阅读(433)  评论(0编辑  收藏  举报