并查集(集并查)

来源:互联网 发布:htc m8 刷Windows教程 编辑:程序博客网 时间:2024/06/09 17:37

并查集其实应该是“集并查”——集合的并和查
——强哥
外国老师讲课怕学生听不懂显得自己不专业,中国某些老师教课怕学生听懂了,显得自己不专业。所以老外讲课通俗易懂,某些老师讲课全是专业名词,弄得学生一头雾水。
——改自网易云课堂一网友评论
网友的这句话从并查集的翻译上确实可见一斑呀!
Description
若某个家族人员过于庞大,要判断两个是否是亲戚,确实还很不容易,给出某个亲戚关系图,求任意给出的两个人是否具有亲戚关系。 规定:x和y是亲戚,y和z是亲戚,那么x和z也是亲戚。如果x,y是亲戚,那么x的亲戚都是y的亲戚,y的亲戚也都是x的亲戚。
Input
第一行:三个整数n,m,p,(n< =5000,m< =5000,p< =5000),分别表示有n个人,m个亲戚关系,询问p对亲戚关系。 以下m行:每行两个数Mi,Mj,1< =Mi,Mj< =N,表示Mi和Mj具有亲戚关系。 接下来p行:每行两个数Pi,Pj,询问Pi和Pj是否具有亲戚关系。
Output
P行,每行一个’Yes’或’No’。表示第i个询问的答案为“具有”或“不具有”亲戚关系。
测试样例:
(2,4) {2,4}
(5,7) {2,4} {5,7}
(1,3) {1,3} {2,4} {5,7}
(8,9) {1,3} {2,4} {5,7} {8,9}
(1,2) {1,2,3,4} {5,7} {8,9}
(5,6) {1,2,3,4} {5,6,7} {8,9}
(2,3) {1,2,3,4} {5,6,7} {8,9}

#include<iostream>using namespace std;int pre[50002],n,m,p,a,b;int find(int x){//找祖先,为了方便后面压缩路径,用循环不用递归    int i=x;    while(pre[i]!=i)        i=pre[i];    int j=x,t;    while(pre[j]!=i){        t=pre[j];        pre[j]=i;        j=t;    }    return i;}void join(int a,int b){//将互为亲戚的两个元素放到一个集合,即祖先相同    a=find(a);    b=find(b);    if(a!=b)        pre[a]=pre[b];}int main(){    cin >> n >> m >> p;    int i;    for(i=1;i<=n;i++)//注意,初始化很容易忘掉        pre[i]=i;    for(i=1;i<=m;i++){        cin >> a >> b;        join(a,b);    }    for(i=1;i<=p;i++){        cin >> a >> b;        if(find(a)==find(b))            cout << "Yes" <<endl;        else            cout << "No" << endl;    }    return 0;}

上一道例题:)
7-7 朋友圈(25 分)

某学校有N个学生,形成M个俱乐部。每个俱乐部里的学生有着一定相似的兴趣爱好,形成一个朋友圈。一个学生可以同时属于若干个不同的俱乐部。根据“我的朋友的朋友也是我的朋友”这个推论可以得出,如果A和B是朋友,且B和C是朋友,则A和C也是朋友。请编写程序计算最大朋友圈中有多少人。

输入格式:

输入的第一行包含两个正整数N(≤30000)和M(≤1000),分别代表学校的学生总数和俱乐部的个数。后面的M行每行按以下格式给出1个俱乐部的信息,其中学生从1~N编号:

第i个俱乐部的人数Mi(空格)学生1(空格)学生2 … 学生Mi

输出格式:

输出给出一个整数,表示在最大朋友圈中有多少人。

输入样例:
7 4
3 1 2 3
2 1 4
3 5 6 7
1 6

输出样例:
4

思路:
这道题是第一次实验室训练赛做的,当时忘了并查集的思路,看了这篇博客才想起来。当时看到这个题,马上就想到了要用能压缩路径的并查集,直接用并查集肯定是通不过的,但我学到的并查集的这个压缩路径写的很不好,他是当作副产品加进来的,在找祖先的时候顺便压缩路径,所以会遗漏部分元素(具体哪部分我也没有验证),这些元素的祖先并不是最大的(即这些元素的祖先还有祖先)。这个是我刚接触并查集的时候思考过的问题,所以印象比较深刻,当时找到了解决办法,就是最后把所有的元素的祖先都找一遍。
感想:
还记得暑假时,那个炎热的下午,上完集训队的课,和儿子一起爬回浮山公寓,然后牺牲了午睡时间就开始搞了半下午的并查集,多亏了当时的思考,比赛时才能游刃有余。以后遇到问题还是多思考!

#include<iostream>#include<cstdio>#include<cstring>#include<vector>#include<cmath>using namespace std;int pre[30001];int find(int x){    int i=x;    while(pre[i]!=i)        i=pre[i];    int j=x,t;    while(pre[j]!=i){        t=pre[j];        pre[j]=i;        j=t;    }    return i;}void join(int a,int b){    a=find(a);    b=find(b);    if(a!=b)        pre[a]=pre[b];}int main(){    int book[30001]={};    int n,m;    int max=0;    cin >> n >> m;    for(int i=1;i<=n;i++){        pre[i]=i;    }    for(int i=0;i<m;i++){        int q;        cin >> q;        int x,y;        cin >> x;        for(int j=1;j<q;j++){            cin >> y;            join(x,y);        }    }    for(int i=1;i<=n;i++)        find(i);    for(int i=1;i<=n;i++){        book[pre[i]]++;        if(book[pre[i]]>max)            max=book[pre[i]];    }    cout << max;    return 0;}
原创粉丝点击