树的直径求解

这篇博客介绍了如何计算有权值树的直径,分别通过两次搜索(BFS或DFS)和树形DP两种方法进行。两次搜索方法适用于非负边权,而树形DP则能处理负边权情况。代码示例展示了如何实现这两种方法,并在最后给出了求解树的直径的思路和证明链接。
摘要由CSDN通过智能技术生成

定义:
树的直径的定义:
在一棵树中,每一条边都有权值,树中的两个点之间的距离,定义为连接两点的路径上边权之和,那么树上最远的两个点,他们之间的距离,就被称之为,树的直径。

树的直径的别称,树的最长链。

请注意:树的直径,还可以认为是一条路径,不一定是只是一个数值。

树的直径一般有两种求解方法:
1、两次搜索(BFS和DFS都可以)
优点 : 可以通过一个新的数组记录路径信息(例如父节点与子节点之间的关系)
缺点 : 无法处理 负边权(遇到 负边权 凉凉)
思路:先任选一点,搜索找到距离该点最远的的点,该点必是树的直径的端点之一,然后以该点为起点,找到距离该点最远的点,即为树的直径。
证明在这里或者这里

#pragma GCC optimize(3,"Ofast","inline")
#include<bits/stdc++.h>
using namespace std;
ll head[300000];
struct node
{
    ll to,pre,w;
} edge[500000];
ll dist[300000];
ll cnt=1,maxx;
void add(ll u,ll v,ll w)
{
    edge[cnt].to=v;
    edge[cnt].w=w;
    edge[cnt].pre=head[u];
    head[u]=cnt++;
}
ll dfs(ll u,ll father,ll distance)
{//u是当前节点,father是其父节点,distance是u到源点的距离
    dist[u]=distance;
    for(ll i=head[u]; i!=-1; i=edge[i].pre)
    {//向下寻找子节点
        ll v=edge[i].to;
        if(v!=father)
        {
            dfs(v,u,distance+edge[i].w);
        }
    }
}
int main()
{
    ll n,u,v,w;
    memset(head,-1,sizeof(head));
    scanf("%lld",&n);
    for(ll i=1; i<=n-1; i++)
    {
        scanf("%lld%lld%lld",&u,&v,&w);
        add(u,v,w);
        add(v,u,w);
    }
    dfs(1,-1,0);//第一个点没有父节点,所以为-1
    u=1;
    for(ll i=1; i<=n; i++)
    {
        if(dist[i]>dist[u])
        {
            u=i;
        }
    }
    dfs(u,-1,0);
    for(ll i=1; i<=n; i++)
    {
        if(dist[i]>dist[u])
        {
            u=i;
        }
    }
    printf("%lld\n",dist[u]);
    return 0;
}

2、树形dp
证明在这里或者这里
优点 : 可以有效处理 负边权
缺点 : 对于记录路径的信息效率较低

#pragma GCC optimize(3,"Ofast","inline")
#include<bits/stdc++.h>
using namespace std;
ll head[300000];
struct node
{
    ll to,pre,w;
} edge[500000];
ll dist[300000];
ll cnt=1,maxx;
void add(ll u,ll v,ll w)
{
    edge[cnt].to=v;
    edge[cnt].w=w;
    edge[cnt].pre=head[u];
    head[u]=cnt++;
}
ll dp(ll u,ll father)
{
    for(ll i=head[u]; i!=-1; i=edge[i].pre)
    {
        ll v=edge[i].to;
        if(v!=father)
        {
            dp(v,u);
            maxx=max(maxx,dist[u]+dist[v]+edge[i].w);
            dist[u]=max(dist[u],dist[v]+edge[i].w);
        }
    }
}
int main()
{
    ll n,u,v,w;
    memset(head,-1,sizeof(head));
    scanf("%lld%lld",&n,&k);
    for(ll i=1; i<=n-1; i++)
    {
        scanf("%lld%lld%lld",&u,&v,&w);
        add(u,v,w);
        add(v,u,w);
    }
    dp(1,-1);
    printf("%lld\n",maxx);
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值