並查集

題目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;
}

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