并查集(与应用举例)

并查集:管理元素分组情况的数据结构。

主要操作:1. 查询元素 A 和 B 是否同一组。 2. 合并元素 A 和 元素 B 所在的组。

#include <iostream>
using namespace std;
const int N = 100;
int Parent[N], Rank[N];

void init(int n) {
    for (int i = 0; i < n; ++i) {
        Parent[i] = i;
        Rank[i] = 1;
    }
}
int find(int x) {
    if(Parent[x] == x)
        return x;
    else return Parent[x] = find(Parent[x]);
}
void unite(int x, int y) {
    x = find(x);
    y = find(y);
    if(x == y) return;
    if(Rank[x] < Rank[y]) {
        Parent[x] = y;
    } else {
        Parent[y] = x;
        if(Rank[x] == Rank[y]) Rank[x]++;
    }
}
bool same(int x, int y) {
    return find(x) == find(y);
}

int main () {
    init(10);
    unite(1, 2);
    unite(2, 3);
    unite(3, 4);
    unite(4, 5);
    unite(5, 6);
    cout << Rank[find(6)] << endl << same(1, 6) << endl;
    return 0;
}

应用举例: N(0 <= N <= 10000) 个人的编号依次为 0 ~ N-1,现在对这 N 个人的成绩排名,可用的关于排名的信息有 M(0 <= M <= 20000) 条,这些信息可能有三种情况,分别是"A > B","A = B","A < B", 分别表示A的Rating高于B, 等于B, 小于B。 要求: 根据这些信息是否能够确定出这个高手榜,是的话就输出"OK"。否则就请你判断出错的原因,到底是因为信息不完全(输出"UNCERTAIN"),还是因为这些信息中包含冲突(输出"CONFLICT")。
注意,如果信息中同时包含冲突且信息不完全,就输出"CONFLICT"。

Link: (http://acm.hdu.edu.cn/showproblem.php?pid=1811)

思路:首先,并查集将相等的归类,每类中只拿出根节点来比就好。

其次,构建有向图(每条边从Rating 高的指向 Rating 低的),此处用邻接表表示(此时注意查 冲突【若是同类中两个人却表现不同大小,则 冲突】)。

最后,拓扑排序。若是没有一个序列(有环)则 冲突;若是有多个序列(同时出现多个点入度为 0),则 信息不全; 若只有一个序列,则 OK

#include <iostream>
#include <vector>
#include <queue>
using namespace std;
const int MAX_N = 10000;
int par[MAX_N], indegree[MAX_N]; // For Union-Find Sets
/* Adjacent list For Topological sort */
vector<vector<int> > Pos(MAX_N, vector<int>());
/************************************************************************/
/*      并查集的基本操作                                                */
/************************************************************************/
void init(int n) {
    for (int i = 0; i < n; ++i) {
        par[i] = i;
        indegree[i] = 0;
    }
}
int find(int x) {
    if (par[x] == x) {
        return x;
    } else {
        return par[x] = find(par[x]);
    }
}
void unite(int x, int y) {
    x = find(x);
    y = find(y);
    if (x == y) return;
    par[y] = x;
}
bool same(int x, int y) {
    return find(x) == find(y);
}
/********************************************************************************/
void insert (int x, int y) {
    Pos[x].push_back(y);
}
int main () {
    int N, M;
    cin >> N >> M;
    init(N);
    int cnt = N;
    vector<int> L(M), R(M);
    vector<char> Signs(M);
    for (int i = 0; i < M; ++i) {
        cin >> L[i] >> Signs[i] >> R[i];
        if (Signs[i] == ‘=‘) {
            unite(L[i], R[i]);
            --cnt;
        }
    }
    bool conflict = false;
    for (int i = 0; i < M; ++i) {
        if (Signs[i] == ‘=‘) continue;
        if (find(L[i]) == find(R[i])) {
            cout << "CONFLICT" << endl;
            conflict = true;
            break;
        } else if (Signs[i] == ‘>‘) {
            insert(find(L[i]), find(R[i]));
            ++indegree[find(R[i])];
        } else {
            insert(find(R[i]), find(L[i]));
            ++indegree[find(L[i])];
        }
    }
    if (conflict) return 0;
    queue<int> qu;
    for (int i = 0; i < N; ++i) {
        if (indegree[i] == 0 && i == find(i)) {
            qu.push(i);
            --cnt;
        }
    }
    bool uncertain = false;
    while(!qu.empty()) {
        if(qu.size() > 1) {
            uncertain = true;
            break;
        }
        int cur = qu.front();
        for (size_t i = 0; i < Pos[cur].size(); ++i) {
            if(--indegree[Pos[cur][i]] == 0) {
                qu.push(Pos[cur][i]);
                --cnt;
            }
        }
        qu.pop();
    }
    if (cnt > 0) {
        cout << "CONFLICT" << endl;
        return 0;
    } else if (uncertain) {
        cout << "UNCERTAIN " << endl;
        return 0;
    } else {
        cout << "OK" << endl;
    }
    return 0;
}
时间: 2024-09-20 02:44:09

并查集(与应用举例)的相关文章

poj 1182 食物链 并查集好题

挑战程序设计上有解答的并查集好题.把事件作为元素进行合并,举例:若输入1 2 3,意思就是把2,3归为同一类,至于归于哪一类并不需要去讨论,则把2属于A,3属于A这两件事件归为一类;2属于B,3属于B这两件事归为一类;2属于C,3属于C这两件事归为一类:若输入 2 2 3,由于A吃B,B吃C,C吃A,就把2属于A,3属于B这两件事情归为一类:以此类推.当检测到当前情况与之前正确的情况不符合,则错误的情况数加1. #include <iostream> #include <cstdio&g

零基础学并查集算法

并查集是我暑假从高手那里学到的一招,觉得真是太精妙的设计了.以前我无法解决的一类问题竟然可以用如此简单高效的方法搞定.不分享出来真是对不起party了.(party:我靠,关我嘛事啊?我跟你很熟么?) 来看一个实例,杭电1232畅通工程 首先在地图上给你若干个城镇,这些城镇都可以看作点,然后告诉你哪些对城镇之间是有道路直接相连的.最后要解决的是整幅图的连通性问题.比如随意给你两个点,让你判断它们是否连通,或者问你整幅图一共有几个连通分支,也就是被分成了几个互相独立的块.像畅通工程这题,问还需要修

hdu 3038 How Many Answers Are Wrong(种类并查集)

了解了种类并查集,同时还知道了一个小技巧,这道题就比较容易了. 其实这是我碰到的第一道种类并查集,实在不会,只好看着别人的代码写.最后半懂不懂的写完了.然后又和别人的代码进行比较,还是不懂,但还是交了. 现在回过头来看,又看了一遍. 题意—— 输入—— 给出多组测试数据. 每组数据第一行包含两个整数n, m.n表示共有1——n这么多个数,m表示m组提示. 接下来m行,每行包含三个整数a, b, val.表示从a到b这几个数的和为val. 这几组数有可能有冲突,问一共有多少组有冲突的数据. 输出—

poj1182食物链,经典带权并查集

动物王国中有三类动物A,B,C,这三类动物的食物链构成了有趣的环形.A吃B, B吃C,C吃A. 现有N个动物,以1-N编号.每个动物都是A,B,C中的一种,但是我们并不知道它到底是哪一种. 有人用两种说法对这N个动物所构成的食物链关系进行描述: 第一种说法是"1 X Y",表示X和Y是同类. 第二种说法是"2 X Y",表示X吃Y. 此人对N个动物,用上述两种说法,一句接一句地说出K句话,这K句话有的是真的,有的是假的.当一句话满足下列三条之一时,这句话就是假话,否

hdu 1829 A Bug&#39;s Life (基础种类并查集)

先说说种类并查集吧. 种类并查集是并查集的一种.但是,种类并查集中的数据是分若干类的.具体属于哪一类,有多少类,都要视具体情况而定.当然属于哪一类,要再开一个数组来储存.所以,种类并查集一般有两个数组,一个存并查集内的父子关系,一个存各个节点所属的种类关系. 以这道题为例(题意在后面,如果没有读题,可以先看完题在来看这部分)—— 这道题很明显,将bug分成两类,一公一母.但是实际上我们并不关心它是公的还是母的,只关心它们之间是同性还是异性.所以,我们可以设与并查集的根节点同性的为0,反之为1.所

POJ 1182 食物链 (带权并查集 &amp;&amp; 向量偏移)

题意 : 中文题, 直接去POJ  1182看即可 分析 : 通过普通并查集的整理归类, 能够单纯地知道某些元素是否在同一个集合内.但是题目不仅只有种类之分, 还有种类之间的关系, 即同类以及吃与被吃, 而且重点是题目问的并不是种类是否在一个集合内, 而是考察给出的关系是否矛盾.在解释之前, 先明白一个问题, 对于给出的关系, 如果我不能通过前面的信息来推断出来, 是不是不能够判断现在给出关系的对错?那就将这个信息作为真命题并存储起来, 方便后面判断.有了刚刚前面的陈述, 可以知道两个东西=>

poj 1182 食物链 &amp;amp;&amp;amp; nyoj 207(种类并查集)

食物链 Time Limit: 1000MS   Memory Limit: 10000K Total Submissions: 52414   Accepted: 15346 Description 动物王国中有三类动物A,B,C,这三类动物的食物链构成了有趣的环形.A吃B. B吃C,C吃A. 现有N个动物.以1-N编号. 每一个动物都是A,B,C中的一种,可是我们并不知道它究竟是哪一种. 有人用两种说法对这N个动物所构成的食物链关系进行描写叙述: 第一种说法是"1 X Y".表示X

并查集(Disjoint Set)

http://www.cnblogs.com/cyjb/p/UnionFindSets.html http://blog.csdn.net/dm_vincent/article/details/7655764 http://blog.csdn.net/dm_vincent/article/details/7769159 并查集(Union-find Sets)是一种非常精巧而实用的数据结构,它主要用于处理一些不相交集合的合并问题.一些常见的用途有求连通子图.求最小生成树的 Kruskal 算法和

并查集 路径压缩(具体解释)

拿HDU 1232举例. 题解: 首先在地图上给你若干个城镇.这些城镇都能够看作点,然后告诉你哪些对城镇之间是有道路直接相连的.最后要解决的是整幅图的连通性问题.比方任意给你两个点,让你推断它们是否连通,或者问你整幅图一共同拥有几个连通分支,也就是被分成了几个互相独立的块.像畅通project这题,问还须要修几条路.实质就是求有几个连通分支.假设是1个连通分支,说明整幅图上的点都连起来了,不用再修路了.假设是2个连通分支,则仅仅要再修1条路,从两个分支中各选一个点,把它们连起来,那么全部的点都是