• 树的直径与重心


    树的直径与重心

    树的直径求解方法一

    思路

    先选取一个点rt作为根节点,dfs去找到一个最长路径的点U,然后通过这个点去dfs,找到路径最长的点V,U->V就是这课树的直径。

    证明正确性:

    假如rt在直径上的话,最长路径的点U一定是直径的一个端点,这一点是显然的。

    假如rt不在直径上,那么从这个点出发也一定可以找到一条路到达直径上一点,接下来就如同上面一样了,无非就是在真正的dis上再加上一段固定的value,对我们最后的直径端点查找并无影响。

    代码

    #include <bits/stdc++.h>
    
    using namespace std;
    
    typedef long long ll;
    const int N = 1e5 + 10;
    
    int head[N], to[N], value[N], nex[N], cnt;
    int n, ans, dis[N];
    
    inline ll read() {
        ll x = 1, s = 0; char c;
        c = getchar();
        while(c < '0' || c > '9') {
            if(c == '-')    x = -1;
            c = getchar();
        }
        while(c >= '0' && c <= '9') {
            s = (s << 1) + (s << 3) + (c ^ 48);
            c = getchar();
        }
        return x * s;
    }
    
    void add(int x, int y, int w) {
        to[cnt] = y;
        nex[cnt] = head[x];
        value[cnt] = w;
        head[x] = cnt++;
    }
    
    void dfs(int rt, int fa) {
        for(int i = head[rt]; ~i; i = nex[i]) {
            if(to[i] == fa) continue;
            dfs(to[i], rt);
            dis[to[i]] = dis[rt] + value[i];
        }
    }
    
    int main() {
        n = read();
        int x, y, w;
        memset(head, -1, sizeof head);
        for(int i = 1; i < n; i++) {
            x = read(), y = read(), w = read();
            add(x, y, w);
            add(y, x, w);
        }
        dfs(1, 0);
        int u = 0, v = 0;
        for(int i = 1; i <= n; i++)
            if(dis[i] > dis[u])   u = i;
        memset(dis, 0, sizeof dis);
        dfs(u, 0);
        for(int i = 1; i <= n; i++)
            if(dis[i] > dis[v]) v = i;
        printf("%d %d %d
    ", u, v, dis[v]);
        return 0;
    }
    

    树的直径求解方法二

    思路

    以根节点出发,去寻找到这个根节点的最长路,以及次长路,得到的两个节点就是树的直径的端点。如果我们要得到直径的数值,还应该要做一次dfs,因为我们一开始选定的根节点无法保证是不是在直径上。

    代码

    #include <bits/stdc++.h>
    
    using namespace std;
    
    typedef long long ll;
    const int N = 1e5 + 10;
    
    int head[N], to[N], value[N], nex[N], cnt;
    int n, ans, fir[N], sec[N], firnode[N], secnode[N];
    
    inline ll read() {
        ll x = 1, s = 0; char c;
        c = getchar();
        while(c < '0' || c > '9') {
            if(c == '-')    x = -1;
            c = getchar();
        }
        while(c >= '0' && c <= '9') {
            s = (s << 1) + (s << 3) + (c ^ 48);
            c = getchar();
        }
        return x * s;
    }
    
    void add(int x, int y, int w) {
        to[cnt] = y;
        nex[cnt] = head[x];
        value[cnt] = w;
        head[x] = cnt++;
    }
    
    void dfs(int rt, int fa) {
        firnode[rt] = secnode[rt] = rt;
        for(int i = head[rt]; ~i; i = nex[i]) {
            if(to[i] == fa) continue;
            dfs(to[i], rt);
            if(fir[to[i]] + value[i] > fir[rt]) {//更新最长路
                sec[rt] = fir[rt];
                fir[rt] = fir[to[i]] + value[i];
                firnode[rt] = to[i];
            }
            else if(fir[to[i]] + value[i] > sec[rt]) {//更新次长路
                sec[rt] = fir[to[i]] + value[i];
                secnode[rt] = to[i];
            }
        }
    }
    
    int main() {
        n = read();
        int x, y, w;
        memset(head, -1, sizeof head);
        for(int i = 1; i < n; i++) {
            x = read(), y = read(), w = read();
            add(x, y, w);
            add(y, x, w);
        }
        dfs(1, 0);
        printf("%d %d %d", firnode[1], secnode[1], fir[1] + sec[1]);//假定我们选择的点是直径上的点。
        return 0;
    }
    

    树的重心

    思路

    找到一个点,其所有的子树中最大的子树节点数最少,那么这个点就是这棵树的重心,删去重心后,生成的多棵树尽可能平衡。

    树中所有点到某个点的距离和中,到重心的距离和是最小的,如果有两个距离和,他们的距离和一样,则这两个点都是重心

    并且,一棵树最多有两个重心,且相邻。

    因此我们可以通过dfs去记录每个节点的子树节点值,然后去统计其最大结点数最小的节点。

    代码

    #include <bits/stdc++.h>
    
    using namespace std;
    
    typedef long long ll;
    const int N = 1e5 + 10;
    
    int head[N], to[N], value[N], nex[N], cnt;
    int n, ans, sz[N], maxn = 0x3f3f3f3f, heart;
    
    inline ll read() {
        ll x = 1, s = 0; char c;
        c = getchar();
        while(c < '0' || c > '9') {
            if(c == '-')    x = -1;
            c = getchar();
        }
        while(c >= '0' && c <= '9') {
            s = (s << 1) + (s << 3) + (c ^ 48);
            c = getchar();
        }
        return x * s;
    }
    
    void add(int x, int y, int w) {
        to[cnt] = y;
        nex[cnt] = head[x];
        value[cnt] = w;
        head[x] = cnt++;
    }
    
    void dfs(int rt, int fa) {
        sz[rt] = 1;
        int now_maxn = 0;
        for(int i = head[rt]; ~i; i = nex[i]) {
            if(to[i] == fa) continue;
            dfs(to[i], rt);
            sz[rt] += sz[to[i]];
            if(sz[to[i]] > now_maxn)    now_maxn = sz[to[i]];//记录最大子节点。
        }
        if(n - sz[rt] > now_maxn) now_maxn = n - sz[rt];//其父节点也算是他的子节点
        if(now_maxn < maxn) heart = rt, maxn = now_maxn;
    }
    
    int main() {
        n = read();
        int x, y, w;
        memset(head, -1, sizeof head);
        for(int i = 1; i < n; i++) {
            x = read(), y = read(), w = read();
            add(x, y, w);
            add(y, x, w);
        }
        dfs(1, 0);
        printf("%d
    ", heart);
        return 0;
    }
    
  • 相关阅读:
    极光推送API简易rails版本
    rake db:migrate出错
    课后作业-阅读任务-阅读提问-5
    课后作业-阅读任务-阅读提问-4
    课后作业-阅读任务-阅读提问-3
    课后作业-阅读任务-阅读提问-2
    课后作业-阅读任务-阅读提问-1
    2017012.01-构建之法:现代软件工程-阅读笔记4
    2017011.17-构建之法:现代软件工程-阅读笔记3
    20170920-构建之法:现代软件工程-阅读笔记1
  • 原文地址:https://www.cnblogs.com/lifehappy/p/12979643.html
Copyright © 2020-2023  润新知