SGU 149(Computer Network)

本文介绍了一种解决树形dp问题的方法,用于求解树中任意两点之间的最长路径。通过两次深度优先搜索(DFS)来计算每个节点到其儿子中的最长距离和次长距离,从而最终得到任意两点间的最大距离。文章还强调了当边为双向时,边数组需适当扩容以避免错误。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题意:求树中任意两点的最长距离;

思路:树形dp,这个也是入门题,不过稍难一些,因为状态转移时需要从父亲或儿子中转移,YY了很久也没什么思路,主要是不知道怎么从父亲中状态转移,看了下别人的分析。。原来需要用到两次dfs,还要维护儿子中最长距离和次最长距离(用set,或vector然后排序),涨姿势了。。千辛万苦总算A了。。推荐一篇比较好的分析,认真看下就懂了(http://hi.baidu.com/avovkozpikgprur/item/70cf22d3a440421c20e2505c)。

还有一个地方需要注意的是,如果建的边是双向,那么edge[]数组务必开成点数的2倍,有一题就因为这个错了好几次,还以为是算法错了、、

#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;

const int N = 10010;
struct Node {
    int e, len, next;
} edge[N];
//dp[N][0]到儿子最长距离,dp[N][1]到儿子次长距离,dp[N][2]到父亲最长距离
struct go {
    int val, idx;
} dp[N][3], p;
int head[N], cnt;

void addedge(int a, int b, int w) {
    edge[cnt].e = b;
    edge[cnt].len = w;
    edge[cnt].next = head[a];
    head[a] = cnt++;
}

void dfs_son(int u, int fa) {//找儿子中的最长距离,和次长距离
    for (int i = head[u]; i != -1; i = edge[i].next){
        int e = edge[i].e, weight = edge[i].len;
        if (e == fa)
            continue;
        dfs_son(e, u);
        p.val = dp[e][0].val + weight, p.idx = e;
		if(p.val > dp[u][0].val){
			dp[u][1] = dp[u][0];
			dp[u][0] = p;
		} else if(p.val > dp[u][1].val)
			dp[u][1] = p;
    }
}

void dfs_father(int u, int fa) {
    for (int i = head[u]; i != -1; i = edge[i].next) {
        int e = edge[i].e, weight = edge[i].len;
        if (e == fa)
            continue;
        int fir_val = dp[u][0].val, fir_idx = dp[u][0].idx;
        int sec_val = dp[u][1].val;
        if (fir_idx == e)
            dp[e][2].val = max(dp[e][2].val, sec_val + weight);
        else if (fir_idx != e)
            dp[e][2].val = max(dp[e][2].val, fir_val + weight);
        dp[e][2].val = max(dp[u][2].val + weight, dp[e][2].val);
        dfs_father(e, u);
    }
}

int main() {
    int n, a, b, i;
    scanf("%d", &n);
    memset(dp, 0, sizeof dp);
    memset(head, -1, sizeof(head));
    for (i = 2; i <= n; i++) {
        scanf("%d%d", &a, &b);
        addedge(a, i, b);
    }
    dfs_son(1, -1);
    dfs_father(1, -1);
    for (i = 1; i <= n; i++)
        printf("%d\n", max(dp[i][0].val, dp[i][2].val));
    return 0;
}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值