zoukankan      html  css  js  c++  java
  • AcWing 846. 树的重心

    题目传送门

    一、题意分析

    1. 树是一种图,图的问题都需要建图,建图无外乎三种办法:邻接矩阵、邻接表、链式前向星

    2. 什么树重心?

      重心是指树中的一个结点,如果将这个点删除后,剩余各个连通块中点数的最大值最小,那么这个节点被称为树的重心。

      树的重心不一定只是一个,有时可以是两个。

    3. 如何求树的重心
      在树上可以进行两种遍历:深度、广度。其中深度遍历可以模拟一支笔在树上画线,直至遍历完成所有节点。我们可以利用这一特点,让每个大臣分派出任务时,都要求下一级的官员返回自己管辖范围内的结点数。然后它自己负责把下级返回的个数加在一起,再加上自己的结点数1,返回给上级调用者。

    4. 无向图就是存两个方向的有向图,比如无向图中两个节点(a,b), 我们就保存(a->b),同时也保存(b->a),就描述了无向图

    5. 如果是稠密图,用邻接矩阵,稀疏图用链式前向星或邻接表。

    二、链式前向星I

    #include <bits/stdc++.h>
    
    using namespace std;
    const int INF = 0x3f3f3f3f;
    const int N = 100010;
    //答案
    int ans = INF;           //剩余各个连通块中点数的最大值最小
    bool st[N];              //是不是走过了
    
    int n;
    int h[N], e[N << 1], ne[N << 1], idx; 
    //h[N]表示有N条单链表的头,e[M]代表每个节点的值,ne[M]代表每个节点的下一个节点号
    
    
    //链式前向星
    void add(int a, int b) {
        e[idx] = b, ne[idx] = h[a], h[a] = idx++;
    }
    
    //函数定义:以u为根的子树的节点数量
    //dfs有一个特点,可以附加的带回这个子树有多少个节点
    int dfs(int u) {
        //防止走回头路
        st[u] = true;
        int sum = 1, res = 0; //sum从1开始,因为自己这个点算第一个点
        //遍历一下当前点的所有出边
        for (int i = h[u]; i != -1; i = ne[i]) {
            int j = e[i];
            if (!st[j]) { //没走过
                int s = dfs(j); //发出任务,让j回答以它为根的节点数量
                sum += s;       //累加的节点数和
                res = max(res, s);//这个节点如果去掉的话,生成的子树节点最多的是多少
            }
        }
        res = max(res, n - sum); //反向建构把它删除后,其它的是不是比由它生成的多
        //哪个方案最少的最大是答案
        ans = min(ans, res);
        return sum;
    }
    
    int main() {
        //树的结点数
        cin >> n;
        //初始化为-1,每个头节点写成-1
        memset(h, -1, sizeof h);
    
        //读入数据,n个结点,n-1条边,建图
        for (int i = 1; i <= n - 1; i++) {
            int a, b;
            cin >> a >> b;
            add(a, b), add(b, a); //无向图,双向建边
        }
        // 从一号节点开始进行搜索,其实,树的封闭的,从哪个点出发都是一样的效果
        dfs(1);
        //输出
        cout << ans << endl;
        return 0;
    }
    

    三、链式前向星II

    #include <bits/stdc++.h>
    
    using namespace std;
    const int INF = 0x3f3f3f3f;
    const int N = 100010;
    int n, m, idx;          //n个点,m条边,idx是新结点加入的数据内索引号
    
    //链式前向星
    struct Edge {
        int to;     //到哪个结点
        int value;  //边权
        int next;   //同起点的下一条边的编号
    } edge[N << 1]; //同起点的边的集合 N<<1就是2*N,一般的题目,边的数量通常是小于2*N的,这个看具体的题目要求
    
    int head[N];    //以i为起点的边的集合入口处
    
    //加入一条边,x起点,y终点,value边权
    void add_edge(int x, int y, int value) {
        edge[++idx].to = y;         //终点
        edge[idx].value = value;    //权值
        edge[idx].next = head[x];   //以x为起点上一条边的编号,也就是与这个边起点相同的上一条边的编号
        head[x] = idx;              //更新以x为起点上一条边的编号
    }
    
    //答案
    int ans = INF;           //剩余各个连通块中点数的最大值最小
    bool st[N];              //是不是走过了
    
    
    //函数定义:以u为根的子树的节点数量
    //dfs有一个特点,可以附加的带回这个子树有多少个节点
    int dfs(int u) {
        //防止走回头路
        st[u] = true;
        int sum = 1, res = 0; //sum从1开始,因为自己这个点算第一个点
        //遍历一下当前点的所有出边
        for (int i = head[u]; i; i = edge[i].next) {
            if (!st[edge[i].to]) { //没走过
                int s = dfs(edge[i].to); //发出任务,让j回答以它为根的节点数量
                sum += s;       //累加的节点数和
                res = max(res, s);//这个节点如果去掉的话,生成的子树节点最多的是多少
            }
        }
        res = max(res, n - sum); //反向建构把它删除后,其它的是不是比由它生成的多
        //哪个方案最少的最大是答案
        ans = min(ans, res);
        return sum;
    }
    
    int main() {
        //树的结点数
        cin >> n;
    
        //读入数据,n个结点,n-1条边,建图
        for (int i = 1; i <= n - 1; i++) {
            int a, b;
            cin >> a >> b;
            //加入到链式前向星
            add_edge(a, b, 1);
            //加入到链式前向星
            add_edge(b, a, 1);
        }
        // 从一号节点开始进行搜索,其实,树的封闭的,从哪个点出发都是一样的效果
        dfs(1);
        //输出
        cout << ans << endl;
        return 0;
    }
    

    四、邻接表

    #include <bits/stdc++.h>
    
    using namespace std;
    const int INF = 0x3f3f3f3f;
    const int N = 100010;
    
    struct Edge {       //记录边的终点,边权的结构体
        int to;         //终点
        int value;      //边权
    };
    int n, m; //表示图中有n个点,m条边
    vector<Edge> p[N];  //使用vector的邻接表
    
    //答案
    int ans = INF;           //剩余各个连通块中点数的最大值最小
    bool st[N];              //是不是走过了
    
    
    //函数定义:以u为根的子树的节点数量
    //dfs有一个特点,可以附加的带回这个子树有多少个节点
    int dfs(int u) {
        //防止走回头路
        st[u] = true;
        int sum = 1, res = 0; //sum从1开始,因为自己这个点算第一个点
        //遍历一下当前点的所有出边
        for (int i = 0; i < p[u].size(); i++) {
            if (!st[p[u][i].to]) { //没走过
                int s = dfs(p[u][i].to); //发出任务,让j回答以它为根的节点数量
                sum += s;       //累加的节点数和
                res = max(res, s);//这个节点如果去掉的话,生成的子树节点最多的是多少
            }
        }
        res = max(res, n - sum); //反向建构把它删除后,其它的是不是比由它生成的多
        //哪个方案最少的最大是答案
        ans = min(ans, res);
        return sum;
    }
    
    int main() {
        //树的结点数
        cin >> n;
    
        //读入数据,n个结点,n-1条边,建图
        for (int i = 1; i <= n - 1; i++) {
            int a, b;
            cin >> a >> b;
            p[a].push_back({b, 1});
            p[b].push_back({a, 1});
        }
        // 从一号节点开始进行搜索,其实,树的封闭的,从哪个点出发都是一样的效果
        dfs(1);
        //输出
        cout << ans << endl;
        return 0;
    }
    
  • 相关阅读:
    闲来无事研究研究.Net中的异步编程
    Sql Server 因为触发器问题导致数据库更新报错“在触发器执行过程中引发了错误,批处理已中止”的问题处理
    c# 连接Redis报错:WRONGTYPE Operation against a key holding the wrong kind of value:类型搞混弄出的错误
    VS2013 调试时出现“表达式计算器中发生内部错误”的问题解决办法
    WCF优化的几个常规思路
    UWP汉堡菜单
    C#注册系统全局快捷键
    CXF详细介绍
    hadoop默认3个核心配置文件说明
    在虚拟机配置hive
  • 原文地址:https://www.cnblogs.com/littlehb/p/15308879.html
Copyright © 2011-2022 走看看