树的直径的两种求法

打多校才发现连树的直径都忘了,赶紧来复习一下树的直径
这里只讲裸的树的直径..
有两种常见的方法去求树的直径

树形dp写法:

第一种是树形dp,我们随便选个结点作为根将有根树转化为无根树,用dp[x]表示以x为根的子树中从x出发的最长链的长度,如果Yi是x的儿子们,那么状态转移是这样的dp[x]=max(dp[x],dp[y]+Edge),接下来考虑经过x的最长链的长度f[x],那么max(f[i],1<=i<=n)就是树的直径了,假设yi,yj是x的两个直接相连的结点,那么f(x)=max(dp[yi]+dp[yj]+x与yi,yj的距离)

具体实现就在下面了

#include <bits/stdc++.h>
using namespace std;
#define de(a) cout << #a << " = " << a << endl
#define rep(i, a, n) for (int i = a; i < n; i++)
#define per(i, a, n) for (int i = n - 1; i >= a; i--)
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int, int> PII;
typedef pair<double, double> PDD;
typedef vector<int, int> VII;
#define inf 0x3f3f3f3f
const ll INF = 0x3f3f3f3f3f3f3f3f;
const ll MAXN = 1e3 + 7;
const ll MAXM = 1e5 + 7;
const ll MOD = 1e9 + 7;
const double eps = 1e-6;
const double pi = acos(-1.0);
int n, m;
struct Edge
{
    int from, to, val;
    int next;
} E[MAXM];
int cnt = -1;
int ans = -inf;
int head[MAXN];
int dp[MAXN]; /* dp[x]表示从结点x出发,往以x作为根的子树走,能够到达的最远距离 */
void add(int from, int to, int val)
{
    E[++cnt].from = from;
    E[cnt].to = to;
    E[cnt].val = val;
    E[cnt].next = head[from];
    head[from] = cnt;
}
void init()
{
    ans = -inf;
    cnt = -1;
    memset(head, -1, sizeof(head));
    memset(dp, 0, sizeof(dp));
}
/* 求树的直径 */
void tree_dp(int u, int fa) /* 随便选个结点作为根结点把树转换成有根树 */
{
    for (int i = head[u]; i != -1; i = E[i].next)
    {
        int to = E[i].to;
        if (fa == to)
            continue;
        tree_dp(to, u);
        ans = max(ans, dp[u] + dp[to] + E[i].val);
        dp[u] = max(dp[u], dp[to] + E[i].val);
    }
}
int main()
{
    init();
    cin >> n >> m;
    for (int i = 0; i < m; i++)
    {
        int u, v, val;
        cin >> u >> v >> val;
        add(u, v, val); //建边
        add(v, u, val);
    }
    tree_dp(1, -1);
    cout << ans << endl;
    return 0;
}

两次dfs或者bfs:

这样写还方便计算出直径上的具体结点。
第一次dfs从任意结点出发对树进行遍历,求出与出发点距离最远的结点记为x
那么从x出发,通过第二次dfs在进行一次遍历,求出与x距离最远的结点y,该结点与结点x得路径长度就是树的直径,这个方法是显然正确的,x肯定是直径的一端,y就是另一端,相连就是树的直径
bfs也差不多...但是不是很想写...

两次dfs具体实现

#include <bits/stdc++.h>
using namespace std;
#define de(a) cout << #a << " = " << a << endl
#define rep(i, a, n) for (int i = a; i < n; i++)
#define per(i, a, n) for (int i = n - 1; i >= a; i--)
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int, int> PII;
typedef pair<double, double> PDD;
typedef vector<int, int> VII;
#define inf 0x3f3f3f3f
const ll INF = 0x3f3f3f3f3f3f3f3f;
const ll MAXN = 1e3 + 7;
const ll MAXM = 1e5 + 7;
const ll MOD = 1e9 + 7;
const double eps = 1e-6;
const double pi = acos(-1.0);
int n, m;
struct Edge
{
    int from, to, val;
    int next;
} E[MAXM];
int cnt = -1;
int ans = -inf;
int head[MAXN];
int vis[MAXN];
int dis[MAXN];
void add(int from, int to, int val)
{
    E[++cnt].from = from;
    E[cnt].to = to;
    E[cnt].val = val;
    E[cnt].next = head[from];
    head[from] = cnt;
}
void init()
{
    ans = -inf;
    cnt = -1;
    memset(head, -1, sizeof(head));
    memset(vis, 0, sizeof(vis));
    memset(dis, 0, sizeof(dis));
}
/* 求树的直径 */
void dfs(int u)
{
    vis[u] = 1;
    for (int i = head[u]; i != -1; i = E[i].next)
    {
        int to = E[i].to;
        if (vis[to])
            continue;
        dis[to] = dis[u] + E[i].val;
        dfs(to);
    }
    return;
}
int main()
{
    init();
    cin >> n >> m;
    for (int i = 0; i < m; i++)
    {
        int u, v, val;
        cin >> u >> v >> val;
        add(u, v, val); //建边
        add(v, u, val);
    }
    dfs(1); /* 第一遍dfs */
    int maxx = -inf, start = -1;
    for (int i = 1; i <= n; i++)
    {
        if (dis[i] > maxx)
        {
            maxx = dis[i];
            start = i;
        }
        dis[i] = vis[i] = 0;
    }
    dfs(start); /* 第二遍dfs */
    for (int i = 1; i <= n; i++)
        ans = max(dis[i], ans);
    cout << ans << endl;
    return 0;
}
/* 7 6
1 2 1
1 3 4
2 4 2
2 5 3
3 6 5
3 7 6 */

转载于:https://www.cnblogs.com/graytido/p/11258010.html

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值