洛谷B4016 树的直径三种解法&U392706 【模板】树的中心题解

第一种

随便找个点进行dfs,走到最深的点就是直径的一个端点从这个端点出发找到最远的点就是直径的另一个端点直径的LCA这个点树的直径一定是某个点的子树的最深和第二深的和

#include<bits/stdc++.h>
using namespace std;
vector<int>g[100086];
int n,tmp,de[100086];
void dfs(int u, int f,int d){
    de[u]=d;
    if(de[u]>de[tmp])tmp=u;
    for (int i = 0; i < g[u].size(); i++){
        int v = g[u][i];
        if (v == f)continue;
        dfs(v, u,d+1);
    }
}
int main(){
    cin>>n;
    for(int i=1;i<=n-1;i++){
        int x,y;
        cin>>x>>y;
        g[x].push_back(y);
        g[y].push_back(x);
    }
    dfs(1,0,0);
    int d1=tmp;
    dfs(d1,0,0);
    cout<<de[tmp];
    return 0;
}

第二种

通过 DFS 遍历树,更新每个节点向下的最长和次长路径,将每个节点的最长路径与次长路径之和比较,最终得到树中最长路径即树的直径。

#include<bits/stdc++.h>
using namespace std;
vector<int>g[100086];
int n,dp[100086][3],ans;
void dfs(int u, int f){
    dp[u][0]=dp[u][1]=0;        // 初始化最长、次长都为0
    for (int i = 0; i < g[u].size(); i++){
        int v = g[u][i];
        if (v == f)continue;    // 不回走父节点
        dfs(v,u);               // 先递归处理子树
        if(dp[v][0]+1 > dp[u][0]){// 用子节点v更新u的最长、次长
            dp[u][1] = dp[u][0];  // 原来的最长降级成次长
            dp[u][0] = dp[v][0]+1;// 新的最长
        }else if(dp[v][0]+1 > dp[u][1]){
            dp[u][1] = dp[v][0]+1;// 更新次长
        }//子树的次大不用关心,子树的最大如果小于我的次大那也不需要了
        ans = max(ans, dp[u][0] + dp[u][1]);// 直径 = 最长 + 次长
    }
}
int main(){
    cin>>n;
    for(int i=1;i<=n-1;i++){
        int x,y;
        cin>>x>>y;
        g[x].push_back(y);
        g[y].push_back(x);
    }
    dfs(1,0);
    cout<<ans;
    return 0;
}

第三种

遍历每个子树,一边记录每个点往下能走的最远距离,一边把两个不同子树的最长路径拼起来(最长链+次长链),记录最大的拼接结果,就是树的直径。

#include<bits/stdc++.h>
using namespace std;
vector<int>g[100086];
int n,tmp,de[100086];
void dfs(int u, int f){
    dp[u]=0;                        // 初始化:u自己到自己距离为0
    for (int i = 0; i < g[u].size(); i++){  // 遍历 u 的所有邻居
        int v = g[u][i];             
        if (v == f)continue;         // 不往回走父节点
        dfs(v,u);                    // 先递归把子树 v 处理完
        ans=max(ans,dp[u]+dp[v]+1);//更新直径:用最长链+次长链+1 
        dp[u]=max(dp[u],dp[v]+1);    
    }
}
int main(){
    cin>>n;
    for(int i=1;i<=n-1;i++){
        int x,y;
        cin>>x>>y;
        g[x].push_back(y);
        g[y].push_back(x);
    }
    dfs(1,0,0);
    int d1=tmp;
    dfs(d1,0,0);
    cout<<de[tmp];
    return 0;
}

U392706 【模板】树的中心题解

首先理解什么是树的中心,理解了就很简单了

例子

假设直径路径是:A —3→ B —4→ C —5→ D —6→ E边权分别是 3、4、5、6,总直径长度 = 3+4+5+6 = 18

max找出半径上每个节点单独拎起时作为根的树最深的节点的深度max(到d1和到d2的深度),后min比较出得出每个节点对应的树最深深度中的minx,最后再遍历对应保存排序输出;

  • 从 A 出发:

    • A: max(0, 18) = 18

    • B: max(3, 15) = 15

    • C: max(7, 11) = 11

    • D: max(12, 6) = 12

    • E: max(18, 0) = 18

  • 可以看到 C 点的 max (7,11)=11 是最小的,所以 C 就是这棵树的中心

  • #include<bits/stdc++.h>
    using namespace std;
    const int MAXN = 1e6 + 86; 
    vector<pair<int, long long>> g[MAXN]; 
    long long depth[MAXN]; 
    int father[MAXN],tmp,a[MAXN],cnt;               
    void dfs(int u, int f, long long d) {
        depth[u] = d;
        father[u] = f;
        if (depth[u] > depth[tmp]) {
            tmp = u;
        }
        for (int i = 0; i < g[u].size(); i++) {
            int v = g[u][i].first;
            long long w = g[u][i].second;
            if (v != f) {
                dfs(v, u, d + w);
            }
        }
    }
    
    int main() {
        ios::sync_with_stdio(false);
        cin.tie(nullptr); // 加速
        int n;
        cin >> n;
        for (int i = 1; i <= n-1; i++) {
            int x, y;
            long long z;
            cin >> x >> y >> z;
            g[x].emplace_back(y, z);
            g[y].emplace_back(x, z);
        }
        //两次DFS找直径的两个端点d1和d2
        tmp = 1;
        dfs(1, 0, 0);
        int d1 = tmp;
        tmp = d1;
        dfs(d1, 0, 0);
        int d2 = tmp;
        // 此时depth[d2]是直径长度,father数组是从d1出发的父节点
        // 从d2沿直径走到d1,找到最小的max(到d1距离, 到d2距离)
        long long minx = 0x3f3f3f3f3f3f3f3f; // long long类型的无穷大
        tmp = d2;
        while (true) {
            long long dis1 = depth[tmp];
            long long dis2 = depth[d2] - depth[tmp];
            long long maxn = max(dis1, dis2);
            minx = min(minx, maxn);
            if (tmp == d1) break;
            tmp = father[tmp];
        }
        //再次遍历直径,保存所有满足条件的中心
        tmp = d2;
        cnt = 0;
        while (true) {
            long long dis1 = depth[tmp];
            long long dis2 = depth[d2] - depth[tmp];
            long long maxn = max(dis1, dis2);
            if (maxn == minx) {
                a[++cnt] = tmp;
            }
            if (tmp == d1) break;
            tmp = father[tmp];
        }
        //排序并输出
        sort(a + 1, a + cnt + 1);
        for (int i = 1; i <= cnt; i++) {
            cout << a[i] << '\n';
        }
        return 0;
    }

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值