【树形DP_树的直径】HDU 2196 Computer

 HDU 2196 Computer

题意:给一棵树,求每个结点的最大距离(从该结点到达所有叶子结点中的最大距离)并输出。题目要求输入真的很迷,每一行(从2开始)代表的结点u和输入结点v之间的边的权值是w。嗐,真的迷。读了好久才读懂。QAQ

【今天才知道原来以下做法是求树的直径的做法,first[x] + upp[x]的最大值就是树的直径】

 

思路:我们任取一个结点作为根结点,将无根树转化为有根树。

写在前:

既然我们转为了有根树,那么一个结点的最大距离无非有两个来源,一个是它的子树中的最大距离,再一个就是它父亲结点所在子树的最大距离。

那么如何来找呢?首先有根树中一个结点,它子树中的最大距离是很好做的,跑一次dfs,和【树的重心】题目是差不多的深搜,只是维护的信息变成了最大距离。

那么往上的父亲结点所在子树的最大距离呢?

【【【下面举个例子解释一下!

  1. first[ i ]: 以 i 为根结点的子树的正向最大距离。

  2. second[ i ]: 以 i 为根结点的子树的正向次大距离。【该路径必须是根结点 i 的正向最大距离路径经过的儿子结点发出的】

  3. son_vis[ i ]: 以 i 为根结点的正向最大距离的儿子结点

  4. upp[ i ]: i 结点向上的子树的最大距离

以上图A结点为例:first[A] = 3很明显,路径为A->C->E->F。那second[ A ] 到底是等于 1还是等于2呢?答案其实是等于1,路径为A->B. 我们知道son_vis[A] = C. 但是second维护的是除开正向最大距离所在子树的儿子结点外的正向次大距离。所以正向次大距离的维护不能再经过C结点。我们可以很容易得到upp[A] = 0.

那second[ ]的意义在哪里呢?(我就是一直没搞懂www)。

好那我们再来康康C结点。first[C] = 2,second[C] = 1,son_vis[C] = E。那么upp[C] = 2. 那么这个是怎么得来的呢?upp[C] = second[A] + 1(1是AC边的权值,我们设为的1)

我们再来康康E结点。first[E] = 1,second[E] = 0(没有次大距离),son_vis[E] = F. 那么upp[E] = 3. 那么这个又是怎么得来的呢?upp[E] = upp[C] + 1(1同样是CE边的权值,我们设为的1)

所以有点明白second的作用没?对于C结点来说,假设我们真的second[A] = 2记的是A->C->D路径,那么second就失去了它本来的用处:用来更新upp[C]的值,所以正向次大距离一定是除开正向最大距离经过的子结点的

 

 

 上面的结点C、E就给出了如果该结点是其父亲结点最大距离路径经过的子结点的DP方程

upp[x] = max(upp[x_root],second[x_root]) + w[root][x];

 再来看看结点D,upp[D] = upp[C]/first[C] + 1(这里1仍然是边CD的权值)

所以我们可以得到该结点不是其父亲结点最大距离路径经过的子结点的DP方程

upp[x] = max(upp[x_root], first[x_root]) + w[root][x];

所以结点x的最大距离

max(upp[x], first[x]);

所以我们需要跑两次dfs,一次是向下,一次是向上。向下的时候是已知叶子节点信息向上递推父亲结点的信息。向上的时候是已知根结点信息,向下更新儿子结点的信息。

END

#include <iostream>
#include <algorithm>
#include <cstdio>
#include <cmath>
#include <cstring>
#include <string>
#include <limits>
#include <set>
#include <queue>
#include <vector>
#include <stack>
#include <map>
#define INF 0x3f3f3f3f

using namespace std;
typedef long long ll;
typedef unsigned long long ull;
const int maxN = 1e4 + 5;
int n;
int dis[maxN];
struct EDGE{
    int adj, to, val;
    EDGE(int a = -1, int b = 0, int c = 0): adj(a), to(b), val(c) {}
}edge[maxN << 1];
int head[maxN], cnt;
void add_edge(int u, int v, int w)
{
    edge[cnt] = EDGE(head[u], v, w);
    head[u] = cnt ++;
}
int first[maxN];
int second[maxN];
int son_vis[maxN];//son_vis[i]:i为根结点的时候其正向最大距离经过的儿子节点
int upp[maxN];
void init()
{
    memset(head, -1, sizeof(head));
    cnt = 0;
    memset(son_vis, -1, sizeof(son_vis));
}
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] + edge[i].val)
        {
            second[x] = first[x];
            first[x] = first[edge[i].to] + edge[i].val;
            son_vis[x] = edge[i].to;
        }
        else if(second[x] < first[edge[i].to] + edge[i].val)
            second[x] = first[edge[i].to] + edge[i].val;
    }
}
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(upp[x], first[x]) + edge[i].val;
        }
        else
        {
            upp[edge[i].to] = max(upp[x], second[x]) + edge[i].val;
        }
        dfs_up(edge[i].to, x);
    }
}
int main()
{
    while(~scanf("%d", &n))
    {
        init();
        for(int i = 0; i < n - 1; i ++ )
        {
            int u = i + 2, v, w;
            scanf("%d%d", &v, &w);
            add_edge(u, v, w);
            add_edge(v, u, w);
        }
        dfs_down(1, 0);
        upp[1] = 0;
        dfs_up(1, 0);
        for(int i = 1; i <= n; i ++ )
        {
            printf("%d\n", max(first[i], upp[i]));
        }
    }
    return 0;
}
/*
8
1 1
2 1
2 1
4 1
5 1
5 1
3 1
 */

下面的是直接暴力跑每个结点最大距离的代码。不用说肯定T了。O(n*n)的复杂度。但是还是想贴上。因为不知道为什么今天下午真的就卡这个暴力卡了好久。一直就想不到怎么实现。最后还是想到了树的重心的实现,于是我觉得是改对了。虽然果不其然T了。 

#include <iostream>
#include <algorithm>
#include <cstdio>
#include <cmath>
#include <cstring>
#include <string>
#include <limits>
#include <set>
#include <queue>
#include <vector>
#include <stack>
#include <map>
#define INF 0x3f3f3f3f

using namespace std;
typedef long long ll;
typedef unsigned long long ull;
const int maxN = 1e4 + 5;
int n;
int dis[maxN];
struct EDGE{
    int adj, to, val;
    EDGE(int a = -1, int b = 0, int c = 0): adj(a), to(b), val(c) {}
}edge[maxN << 1];
int head[maxN], cnt;
void add_edge(int u, int v, int w)
{
    edge[cnt] = EDGE(head[u], v, w);
    head[u] = cnt ++;
}
void init()
{
    memset(head, -1, sizeof(head));
    cnt = 0;
    memset(dis, -1, sizeof(dis));
}

void dfs(int x, int fa)
{
    for(int i = head[x]; ~i; i = edge[i].adj)
    {
        if(edge[i].to == fa)
            continue;
        dfs(edge[i].to, x);
        dis[x] = max(dis[x], edge[i].val + dis[edge[i].to]);
    }
}

int main()
{
    while(~scanf("%d", &n))
    {
        init();
        for(int i = 0; i < n - 1; i ++ )
        {
            int u = i + 2, v, w;
            scanf("%d%d", &v, &w);
            add_edge(u, v, w);
            add_edge(v, u, w);
        }
        for(int i = 1; i <= n; i ++ )
        {
            memset(dis, 0, sizeof(dis));
            dfs(i, 0);
            printf("%d\n", dis[i]);
        }
    }
    return 0;
}

 来自蒟蒻 2020/02/23温习

#include <iostream>
#include <cstdio>
#include <cstring>
#include <queue>
#define INF 0x3f3f3f3f
using namespace std;

inline int read()
{
    int x = 0, f = 1; char c = getchar();
    while(c < '0' || c > '9') { if(c == '-') f = -f; c = getchar(); }
    while(c >= '0' && c <= '9') { x = x * 10 + c - '0'; c = getchar(); }
    return x * f;
}

const int maxN = 10004;

int n;

struct EDGE{
    int adj, to, w;
    EDGE(int a = -1, int b = 0, int c = 0): adj(a), to(b), w(c){}
}edge[maxN << 1];
int head[maxN], cnt;

void add_edge(int u, int v, int w)
{
    edge[cnt] = EDGE(head[u], v, w);
    head[u] = cnt ++ ;
}

int first[maxN], second[maxN], upp[maxN];
int mxSon[maxN];

void dfs_down(int u, int fa)
{
    first[u] = second[u] = 0;
    for(int i = head[u]; ~i; i = edge[i].adj)
    {
        int v = edge[i].to;
        if(v == fa) continue;
        dfs_down(v, u);
        if(first[u] < first[v] + edge[i].w)
        {
            second[u] = first[u];
            first[u] = first[v] + edge[i].w;
            mxSon[u] = v;
        }
        else if(second[u] < first[v] + edge[i].w)
            second[u] = first[v] + edge[i].w;
    }
}

void dfs_up(int u, int fa)
{
    for(int i = head[u]; ~i; i = edge[i].adj)
    {
        int v = edge[i].to;
        if(v == fa) continue;
        if(mxSon[u] == v)
            upp[v] = max(second[u], upp[u]) + edge[i].w;
        else
            upp[v] = max(first[u], upp[u]) + edge[i].w;
        dfs_up(v, u);
    }
}

void init()
{
    memset(head, -1, sizeof(head));
    for(int i = 0; i <= n; ++ i)
    {
        head[i] = -1;
        first[i] = second[i] = upp[i] = 0;
    }
    cnt = 0;
}

int main()
{
    while(~scanf("%d", &n))
    {
        init();
        for(int i = 2; i <= n; ++ i )
        {
            int v, w; v = read(); w = read();
            add_edge(i, v, w);
            add_edge(v, i, w);
        }
        dfs_down(1, 0);
        dfs_up(1, 0);
        for(int i = 1; i <= n; ++ i )
            printf("%d\n", max(upp[i], first[i]));
    }
    return 0;
}

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值