zoukankan      html  css  js  c++  java
  • 带权并查集

    带权并查集

    普通的并查集仅仅记录的是集合的关系,这个关系无非是同属一个集合或者是不在一个集合

    带权并查集不仅记录集合的关系,还记录着集合内元素的关系或者说是集合内元素连接线的权值

    普通并查集本质是不带权值的图,而带权并查集则是带权的图

    考虑到权值就会有以下问题:

    1. 每个节点都记录的是与根节点之间的权值,那么在Find的路径压缩过程中,权值也应该做相应的更新,因为在路径压缩之前,每个节点都是与其父节点链接着,那个Value自然也是与其父节点之间的权值
    2. 在两个并查集做合并的时候,权值也要做相应的更新,因为两个并查集的根节点不同

    例题

    D - How Many Answers Are Wrong

    题意: 给定一个长度为n的数列以及m条信息,每条信息包含三个数 l,r,v,表示数列的[l,r]区间和为v.这些信息中有的是正确信息,有的是错误信息。对于每一条信息,如果其与之前出现的正确信息有逻辑矛盾,那么它就是错误的,否则是正确的。现在请你判断这m条信息中有多少条错误信息。

    样例

    Sample Input

    10 5
    1 10 100
    7 10 28
    1 3 32
    4 6 41
    6 6 1

    Sample Output

    1

    题解:

    本题利用带权并查集,选择各个端点作为并查集的元素,当两个端点之间的边权值已知时,将两个端点纳入同一个集合,将右端点作为父结点

    首先定义每一个元素的信息

    // 每一个元素的信息包括 1.父结点 2.到父节点的边权
    // 故建立两个数组
    #define Maxsize 200000+1
    int f[Maxsize];
    int dis[Maxsize];
    

    初始化函数

    void init(int n){
      for(int i = 0; i <= n; i++){ //从0开始,因为要记录[0,?]的边权
        f[i] = i;
        dis[i] = 0; // 一开始父结点就是自己,自己到自己的边权是0
      }
    }
    

    查找函数

    int find_f(int a){
      if(f[a] == a){
        return a;
      }else{
        int ori_f = f[a];
        f[a] = find_f(f[a]);
        dis[a] += dis[ori_f]; // 这里画图可以推导出方程
      }
    }
    

    合并函数

    void union_f(int l,int r,int val){ // 当已知l~r的边权之后,调用此函数进行合并
      int lf = find_f(l);
      int rf = find_f(r);
      if(lf < rf){
        f[lf] = rf;
        dis[lf] = dis[r] - dis[l] + val;
        // -------l -----------lf ---  r -------- rf
        //        |------ val-- -------|
        //        |-- dis[l]---|
        //                             |--dis[r]---|
        //   dis[lf] = dis[r] + val - dis[l]
          
          
        // -------l -----------r --- lf -------- rf
        //        |----val-----|
        //        |--------dis[l]----|
        //                     |------dis[r]-----|
        //  dis[lf] = dis[r] + val - dis[l]
      }else{
        f[rf] = lf;
        dis[rf] = dis[l] - dis[r] - val;
        // -------l---------r------------rf------lf
        //        |---val---|
        //        |-----------dis[l]--------------|
        //                  |---dis[r]---|
        // dis[rf] = dis[l] - dis[r] - val
      }
    }
    

    判断函数

    bool same_f(int a,int b){
      return find_f(a) == find_f(b);
    }
    

    完整代码

    #include <iostream>
    #include <algorithm>
    
    #define MaxN 200000+1
    #define MaxM 40000+1
    
    using namespace std;
    
    int f[MaxN];
    int dis[MaxN];
    
    void init(int n){
        for(int i = 0; i <= n; i++){
            f[i] = i;
            dis[i] = 0;
        }
    }
    
    int find_f(int a){
        if(f[a] == a){
            return a;
        }else{
            int ori_f = f[a];
            f[a] = find_f(f[a]);
            dis[a] += dis[ori_f];
            return f[a];
        }
    }
    
    void union_f(int l,int r,int val){ // 当已知l~r的边权之后,调用此函数进行合并
        int lf = find_f(l);
        int rf = find_f(r);
        if(lf < rf){
            f[lf] = rf;
            dis[lf] = dis[r] - dis[l] + val;
        }else{
            f[rf] = lf;
            dis[rf] = dis[l] - dis[r] - val;
        }
    }
    
    int same_f(int l,int r){
        return find_f(l) == find_f(r);
    }
    int main(){
        int n,m,l,r,val;
        while (cin >> n >> m) {
            int ans = 0;
            init(n);
            for (int i = 0; i < m; i++) {
                cin >> l >> r >> val;
                l--; // 点权换边权
                if(same_f(l,r)){
                    if(dis[l]-dis[r] != val){
                        ans++;
                    }
                }else{
                    union_f(l,r,val);
                }
            }
            cout << ans << endl;
        }
        return 0;
    }
    
    

    参考 https://www.cnblogs.com/zhxmdefj/p/11117791.html

    ---- suffer now and live the rest of your life as a champion ----
  • 相关阅读:
    【转】网络字节序与主机字节序
    VC之美化界面篇 (转)
    VS2008编译的程序在某些机器上运行提示“由于应用程序配置不正确,应用程序未能启动”的问题(转)
    符验手记
    一友人昨夜接到电话,发生何事
    [转]众VC论道IT峰会:投资是否靠运气
    路过一个小摊,看到一个有趣的现象
    PJSUA提示要注册线程的解决办法
    彩票股票金融与运气之研究(五) 明敌
    随手测一局婚姻,留验
  • 原文地址:https://www.cnblogs.com/popodynasty/p/13321269.html
Copyright © 2011-2022 走看看