并查集

题目1:好朋友

题目描述
  有一个叫作“ 数码世界” 的奇异空间, 在数码世界里生活着许许多多的数码宝贝, 其中有些数码宝贝之间可能是好朋友。并且数码世界有两条不成文的规定:
  第一, 数码宝贝A和数码宝贝B是好朋友等价于数码宝贝B和数码宝贝A是好朋友。
  第二, 如果数码宝贝A和数码宝贝C是好朋友, 而数码宝贝B和数码气宝贝C也是好朋友, 那么A和B也是好朋友。
  现在给出这些数码宝贝中所有好朋友的信息, 问:可以把这些数码宝贝分成多少组, 满足每组中的任意两只数码宝贝都是好朋友, 且任意两组之间的数码宝贝都不是好朋友。

输入格式
  输入的第一行有两个正整数n (n<=100)和m(m<=100), 分别表示数码宝贝的个数和好朋友的组数, 其中数码宝贝编号为1~n。
  接下来有m行, 每行两个正整数a和b, 表示数码宝贝a和数码宝贝b是好朋友。

输出格式
输出一个整数, 表示这些数码宝贝可以分成的组数。

样例输入1
4 2
1 4
2 3
样例输出1
2

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

AC代码

#include <bits/stdc++.h>
using namespace std;
const int maxn=110;
int n,m,father[maxn];
set<int>num;
//初始化
void init() {
    for(int i=1; i<=n; i++)
        father[i]=i;
}
//查找x所在集合的根节点
int findFather(int v) {
    int a=v;
    while(v!=father[v]) { //找到根节点
        v=father[v];
    }
    while(a!=father[a]) { //重新找一遍根节点,把过路的节点的合并集都指向根节点
    	int z=a;
        a=father[a];
        father[a]=v;
    }
    return v;
}
//合并a,b所在集合
void Union(int a,int b) {
    int faA,faB;
    faA=findFather(a);
    faB=findFather(b);
    if(faA!=faB)//如果不是同一集合
        father[faA]=faB;//A的根接到B上,合并
}
int main() {
    scanf("%d %d",&n,&m);
    int x,y;
    for(int i=0; i<m; i++) {
        scanf("%d %d",&x,&y);
        Union(x,y);
    }
    for(int i=1; i<=n; i++)
        num.insert(findFather(i));
    printf("%d\n",num.size());
    return 0;
}

题目2:问题 A: 通信系统

http://codeup.cn/problem.php?cid=100000615&pid=0
题目描述

  某市计划建设一个通信系统。按照规划,这个系统包含若干端点,这些端点由通信线缆链接。消息可以在任何一个端点产生,并且只能通过线缆传送。每个端点接收消息后会将消息传送到与其相连的端点,除了那个消息发送过来的端点。如果某个端点是产生消息的端点,那么消息将被传送到与其相连的每一个端点。
  为了提高传送效率和节约资源,要求当消息在某个端点生成后,其余各个端点均能接收到消息,并且每个端点均不会重复收到消息。
  现给你通信系统的描述,你能判断此系统是否符合以上要求吗?

输入
  输入包含多组测试数据。每两组输入数据之间由空行分隔。
  每组输入首先包含2个整数N和M,N(1<=N<=1000)表示端点个数,M(0<=M<=N*(N-1)/2)表示通信线路个数。
  接下来M行每行输入2个整数A和B(1<=A,B<=N),表示端点A和B由一条通信线缆相连。两个端点之间至多由一条线缆直接相连,并且没有将某个端点与其自己相连的线缆。
  当N和M都为0时,输入结束。

输出
  对于每组输入,如果所给的系统描述符合题目要求,则输出Yes,否则输出No。

样例输入 Copy

4 3
1 2
2 3
3 4

3 1
2 3

0 0
样例输出 Copy
Yes
No

注意

  额外附加了一个条件,单个端点只接收一次消息,所以,不能有环出现,排除也很简单,根据树的边数为n-1定则,而且要所有端点必须为同一集合,那么M必须等于N-1,否则,所有端点无法连通,或出现环。

AC代码

#include <bits/stdc++.h>
using namespace std;
const int maxn=1010;
int n,m,father[maxn];
int flag;
bool tong;
void init() {
    for(int i=1; i<=n; i++)
        father[i]=i;
}
int findFather(int v) {
    int a=v;
    while(v!=father[v])
        v=father[v];
    while(a!=father[a]) {
    	int z=a;
        a=father[a];
        father[z]=v;
    }
    return v;
}
void Union(int a,int b) {
    int faA,faB;
    faA=findFather(a);
    faB=findFather(b);
    if(faA!=faB)
        father[faA]=faB;
}
int main() {
    int x,y;
    while(scanf("%d %d",&n,&m)!=EOF) {
        if(m==0&&n==0)
            break;
        init();
        tong=true;
        for(int i=0; i<m; i++) {
            scanf("%d %d",&x,&y);
            Union(x,y);
        }
        flag=findFather(1);
        if(m!=n-1)
            tong=false;
        for(int i=1; i<=n&&tong; i++)
            if(flag!=findFather(i))
                tong=false;
        if(tong)
            printf("Yes\n");
        else
            printf("No\n");
    }


    return 0;
}

题目3:问题 D: More is better

http://codeup.cn/problem.php?cid=100000615&pid=3
题目描述

Mr Wang wants some boys to help him with a project. Because the project is rather complex, the more boys come, the better it will be. Of course there are certain requirements.Mr Wang selected a room big enough to hold the boys. The boy who are not been chosen has to leave the room immediately. There are 10000000 boys in the room numbered from 1 to 10000000 at the very beginning. After Mr Wang’s selection any two of them who are still in this room should be friends (direct or indirect), or there is only one boy left. Given all the direct friend-pairs, you should decide the best way.

输入

The first line of the input contains an integer n (0 ≤ n ≤ 100 000) - the number of direct friend-pairs. The following n lines each contains a pair of numbers A and B separated by a single space that suggests A and B are direct friends. (A ≠ B, 1 ≤ A, B ≤ 10000000)

输出

The output in one line contains exactly one integer equals to the maximum number of boys Mr Wang may keep.

样例输入 Copy

3
1 3
1 5
2 5
4
3 2
3 4
1 6
2 6

样例输出 Copy

4
5

省题

  1. After Mr Wang’s selection any two of them who are still in this room should be friends (direct or indirect), or there is only one boy left.

  房子里装了1e7个人,从中选n组人,其他人出去,要求剩下的人都是朋友或者只剩一个人,问教授能留下的最多为多少人(教授的点名中,可能分为了几个集合的朋友圈,选择集合最大的的);
    选了至少一组后那就至少有两个人;
    但还有一种特殊情况是只有一个人:那就是选择了一组同一个人或者,n=0没有选任何朋友关系,那么就是only one boy left
  意思就是n=0的特殊情况应输出1

 if(n==0) {
            printf("1\n");
            continue;
        }
  1. 两个集合合并后的总集合人数就是两个集合人数的相加
  2. 若是在输入状态中遇到两个不需要合并的孩子,说明这两个孩子之前曾经合并过了,属于同一个集合,那就不需要在合并操作做什么
void Union(int a,int b) {
    int faA,faB;
    faA=findFather(a);
    faB=findFather(b);
    if(faA!=faB) {
        father[faA]=faB;
        child[faB]+=child[faA];//合并后的集合=集合A的孩子数+集合B的孩子数
        if(child[faB]>Maxn)
            Maxn=child[faB];
    }
    //若在同一个集合中(重复输出了关系)
    //那说明是之前合并过了的,因为若是新出现的关系都是需要被合并
}
  1. 初始化就是把房间内1~1e7所有人的根节点都指向自己,各自作为集合存在,集合人数都为1,Maxn是来找最大集合的人数的,一开始初始化为0
void init() {
    Maxn=0;//最大集合Maxn=0
    for(int i=1; i<maxn; i++) {
        father[i]=i;//每个节点的根节点一开始都是自己
        child[i]=1;//每个节点所在集合的孩子数都为1,即拥有自己
    }
}

注意

空间复杂度的计算:
  这一题的内存限制为128MB还是比较大的的,设置数组father[maxn],child[maxn]是装得下的的
int型是四个字节
241e7=80MB
时间复杂度的计算:
https://blog.csdn.net/a845717607/article/details/81611408
  这个题目的男孩的编号取值范围在1~1e7,这里,千万别被1e7的数量级给吓住了,如果这里使用map映射,恰巧就中了出题人的下怀,因为这题使用map会超时,原因可能是频繁的进行查找,map的速度毕竟不能达到数组下标直接访问这么快,所以,直接开数组就行,一个1e7的整型数组大小,emmmmm我也不知道大概多大,如果照理论上来算,一个整型4字节1e7大概也就是40000KB左右也就是大约40MB,并没有超过128MB的限制,所以,这题说白了就是在考编程者是否熟悉程序测试的内存限制= = 因为正常程序内存限制为32MB,但是这题却给了128MB,不仔细看还真注意不到。

AC代码

#include <bits/stdc++.h>
using namespace std;
const int maxn=1e7+10;
int n,Maxn,father[maxn],child[maxn]= {0};
void init() {
    Maxn=0;//最大集合Maxn=0
    for(int i=1; i<maxn; i++) {
        father[i]=i;//每个节点的根节点一开始都是自己
        child[i]=1;//每个节点所在集合的孩子数都为1,即拥有自己
    }
}
int findFather(int v) {
    int a=v;
    while(v!=father[v])
        v=father[v];
    while(a!=father[a]) {
        int z=a;
        a=father[a];
        father[z]=v;
    }
    return v;
}
void Union(int a,int b) {
    int faA,faB;
    faA=findFather(a);
    faB=findFather(b);
    if(faA!=faB) {
        father[faA]=faB;
        child[faB]+=child[faA];//合并后的集合=集合A的孩子数+集合B的孩子数
        if(child[faB]>Maxn)
            Maxn=child[faB];
    }
    //若在同一个集合中(重复输出了关系)
    //那说明是之前合并过了的,因为若是新出现的关系都是需要被合并
}
int main() {
    int x,y;
    while(scanf("%d",&n)!=EOF) {
        if(n==0) {
            printf("1\n");
            continue;
        }
        init();
        for(int i=0; i<n; i++) {
            scanf("%d %d",&x,&y);
            Union(x,y);
        }
        printf("%d\n",Maxn);
    }
    return 0;
}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章