【树形DP_树的直径】HDU 4607 Park Visit

HDU 4607 Park Visit

题意:一个有N个结点的无根树,我们要访问K个点,问最小路径是多少。

解:我们设树的直径是dia,直径上经过的结点个数为num

  1. 当K <= num时,ans = K-1. (从直径的一端沿着直径走)

  2. others,ans = dia + (K - num) * 2(直径一定是走完的,还要走直径的分支,并且分支是走重复的,也就是两倍)

举个栗子!!!

如果K == 8 

如果K == 9 

最开始的时候也是不明白K > num的时候到底是什么鬼的公式,但是事实是画个图就明白了。哈哈!!

 

求树的直径

一、两遍dfs的做法 

  1. 以任一个结点为根,将无根树转为有根树,找到它的最大距离,并记到达的结点为nod1。

  2. 以nod1为根,跑dfs,找到它的最大距离,到达的结点为nod2。显而易见nod1和nod2两个结点间的距离就是dia. 

#include <iostream>
#include <cstdio>
#include <cmath>
#include <string>
#include <cstring>
#include <algorithm>
#include <limits>
#include <vector>
#include <stack>
#include <queue>
#include <set>
#include <map>
#define INF 0x3f3f3f3f
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
const int maxN = 1e5 + 5;
int N, Q, k;
int head[maxN], cnt;
struct EDGE{
    int adj, to;
    EDGE(int a = -1, int b = 0): adj(a), to(b) {}
}edge[maxN << 1];
void add_edge(int u, int v)
{
    edge[cnt] = EDGE(head[u], v);
    head[u] = cnt ++;
}
struct DIA{
    int nod1, nod2, dia;
    DIA(int a = 0, int b = 0, int c = 0): nod1(a), nod2(b), dia(c) {}
}info;
void init()
{
    memset(head, -1, sizeof(head));
    cnt = 0;
    info = DIA(0, 0, 0);
}
int dis[maxN];
void dfs(int x, int fa, int d, int flag)
{
    dis[x] = d;
    if(info.dia < dis[x])
    {
        info.dia = dis[x];
        if(flag == 0)
            info.nod1 = x;
        else if(flag == 1)
            info.nod2 = x;
    }
    for(int i = head[x]; ~i; i = edge[i].adj)
    {
        if(edge[i].to == fa)
            continue;
        dfs(edge[i].to, x, d + 1, flag);
    }
}
int main()
{
    int T; scanf("%d", &T);
    while(T -- )
    {
        scanf("%d%d", &N, &Q);
        init();
        for(int i = 0; i < N - 1; i ++ )
        {
            int u, v;
            scanf("%d%d", &u, &v);
            add_edge(u, v);
            add_edge(v, u);
        }
        dfs(1, 0, 0, 0);
        dfs(info.nod1, 0, 0, 1);
        while(Q -- )
        {
            scanf("%d", &k);
            if(k <= info.dia)
                printf("%d\n", k - 1);
            else
                printf("%d\n", info.dia + (k - (info.dia + 1)) * 2);
        }
    }
    return 0;
}

二、树形DP求解树的直径

树形DP详解

#include <iostream>
#include <cstdio>
#include <cmath>
#include <string>
#include <cstring>
#include <algorithm>
#include <limits>
#include <vector>
#include <stack>
#include <queue>
#include <set>
#include <map>
#define INF 0x3f3f3f3f
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
const int maxN = 1e5 + 5;
int N, Q, k;
int head[maxN] ,cnt;
struct EDGE{
    int adj, to;
    EDGE(int a = 0, int b = 0): adj(a), to(b) {}
}edge[maxN << 1];
void add_edge(int u, int v)
{
    edge[cnt] = EDGE(head[u], v);
    head[u] = cnt ++ ;
}
int dia;
int first[maxN], second[maxN], upp[maxN];
int son_vis[maxN];
void dfs_down(int x, int fa)//向下遍历
{
    first[x] = 0;
    second[x]= 0;
    for(int i = head[x]; ~i; i = edge[i].adj)
    {
        if(edge[i].to == fa)
            continue;
        dfs_down(edge[i].to, x);
        if(first[x] < first[edge[i].to] + 1)
        {
            second[x] = first[x];
            first[x] = first[edge[i].to] + 1;
            son_vis[x] = edge[i].to;
        }
        else if(second[x] < first[edge[i].to] + 1)
            second[x] = first[edge[i].to] + 1;
    }
}
void dfs_up(int x, int fa)//向上遍历
{
    for(int i = head[x]; ~i; i = edge[i].adj)
    {
        if(edge[i].to == fa)
            continue;
        if(son_vis[x] == edge[i].to)
            upp[edge[i].to] = max(second[x], upp[x]) + 1;
        else
            upp[edge[i].to] = max(first[x], upp[x]) + 1;
        dfs_up(edge[i].to, x);
    }
    dia = max(dia, upp[x] + first[x]);
}

void init()
{
    memset(head, -1, sizeof(head));
    cnt = 0;
    dia = 0;
    upp[1] = 0;
    memset(son_vis, -1, sizeof(son_vis));
}
int main()
{
    int T; scanf("%d", &T);
    while(T -- )
    {
        scanf("%d%d", &N, &Q);
        init();
        for(int i = 0; i < N -1; i ++ )
        {
            int u, v;
            scanf("%d%d", &u, &v);
            add_edge(u, v);
            add_edge(v, u);
        }
        dfs_down(1, 0);
        dfs_up(1, 0);
        for(int i = 0; i < Q; i ++ )
        {
            scanf("%d", &k);
            if(k <= dia + 1)
                printf("%d\n", k - 1);
            else
                printf("%d\n", dia + (k - (dia + 1)) * 2);
        }
    }
    return 0;
}
发布了180 篇原创文章 · 获赞 54 · 访问量 1万+

猜你喜欢

转载自blog.csdn.net/weixin_44049850/article/details/103898813