並查集—— 朋友圈子數、暢通工程、小希的迷宮、More is better

朋友之間的圈子數、暢通工程類似

題目描述:

某省調查城鎮交通狀況,得到現有城鎮道路統計表,表中列出了每條道路直接連通的城鎮。省政府“暢通工程”的目標是使全省任何兩個城鎮間都可以實現交通(但不一定有直接的道路相連,只要互相間接通過道路可達即可)。問最少還需要建設多少條道路?

Input

測試輸入包含若干測試用例。每個測試用例的第1行給出兩個正整數,分別是城鎮數目N ( < 1000 )和道路數目M;隨後的M行對應M條道路,每行給出一對正整數,分別是該條道路直接連通的兩個城鎮的編號。爲簡單起見,城鎮從1到N編號。當N爲0時,輸入結束,該用例不被處理。

 

Output

對每個測試用例,在1行裏輸出最少還需要建設的道路數目。

Sample Input

4 2
1 3
4 3

3 3
1 2
1 3
2 3

5 2
1 2
3 5

999 0

0

Sample Output

1
0
2
998

 

題意:

還需建多少條路能使所有村子兩兩相通,我們先把已經建好的道路的點合併,如果還需要建道路,那麼所有點組成的集合數量一定大於1。那麼我們只要查詢n個村子構成了多少個集合,即詢問一個點的父節點是否爲本身。最終的集合數-1即是解,爲啥減一,如集合:a    b    c,只需要連接a——b——c,兩條線即可使得集合互通,所以結果爲集合數減一


代碼:

#include<cstdio>
#include<iostream>

using namespace std;
int city[1010];

int find_father(int n)  //查找祖先節點,當節點記錄的祖先是自己,則表示查找到祖先了
{
    if(city[n] == n) {
        return n;
    }
    return find_father(city[n]);
}
void merge_ab(int a,int b)   //合併節點:設置共同祖先
{
    int x = find_father(a);
    int y = find_father(b);
    if(x != y)
        city[y] = x;
}
int main()
{
    int n,m;
    while(~scanf("%d",&n),n)
    {
        scanf("%d",&m);
        for(int i = 0; i <= n; i++)  //最開始的時候,每個節點都是自己的祖先
            city[i] = i;

        int a,b;
        for(int i = 0; i < m; i++) {
            scanf("%d%d",&a,&b);
            merge_ab(a,b);
        }
        int res = 0;
        for(int i = 1; i <= n; i++) {   //n個村子構成的集合數
            if(city[i] == i)
                res++;
        }
        cout << res-1 << endl;
    }
    return 0;
}

 


小希的迷宮

題目描述:

上次Gardon的迷宮城堡小希玩了很久(見Problem B),現在她也想設計一個迷宮讓Gardon來走。但是她設計迷宮的思路不一樣,首先她認爲所有的通道都應該是雙向連通的,就是說如果有一個通道連通了房間A和B,那麼既可以通過它從房間A走到房間B,也可以通過它從房間B走到房間A,爲了提高難度,小希希望任意兩個房間有且僅有一條路徑可以相通(除非走了回頭路)。小希現在把她的設計圖給你,讓你幫忙判斷她的設計圖是否符合她的設計思路。比如下面的例子,前兩個是符合條件的,但是最後一個卻有兩種方法從5到達8。

Input

輸入包含多組數據,每組數據是一個以0 0結尾的整數對列表,表示了一條通道連接的兩個房間的編號。房間的編號至少爲1,且不超過100000。每兩組數據之間有一個空行。整個文件以兩個-1結尾。

 

Output

對於輸入的每一組數據,輸出僅包括一行。如果該迷宮符合小希的思路,那麼輸出"Yes",否則輸出"No"。

Sample Input

6 8  5 3  5 2  6 4
5 6  0 0

8 1  7 3  6 2  8 9  7 5
7 4  7 8  7 6  0 0

3 8  6 8  6 4
5 3  5 6  5 2  0 0

-1 -1
Sample Output

Yes
Yes
No

題意:

1.房間編號並不是1—n,有些數字並沒有出現,所以要用一個數組標記數字是否出現;

2.要判斷所有房間是不是屬於同一個集合。


代碼:

#include<cstdio>
#include<iostream>

using namespace std;
const int maxn = 100010;
int city[maxn];
int flag[maxn];

int find_father(int n)
{
    if(n == city[n])
        return n;
    return find_father(city[n]);
}
bool merge_nm(int n, int m)
{
    int x = find_father(n);
    int y = find_father(m);
    if(x == y)
        return false;
    city[x] = y;
    return true;

}
int main()
{
    int n,m;
    while(~scanf("%d%d",&n,&m))
    {
        if(n == -1 && m == -1)
            break;
        for(int i = 0; i <= 100000; i++) {
            city[i] = i;
            flag[i] = 0;
        }
        bool is = merge_nm(n,m);
        flag[n] = 1;
        flag[m] = 1;
        if(m == 0 && n == 0)       //若一組數據的開頭即是0,0;輸出Yes
            printf("Yes\n");

        while(scanf("%d%d",&n,&m),n&&m)
        {
            if(merge_nm(n,m) == false)   //何必,若返回false,表示有相同的父節點
                is = false;     //記錄一下狀態
            flag[n] = 1;
            flag[m] = 1;
        }

        if(is == false)
            printf("No\n");
        else {
            int res = 0;
            for(int i = 0; i <= 100000; i++) {   //統計存在幾個集合
                if(flag[i] && city[i] == i)
                    res++;
            }
            if(res == 1)
                printf("Yes\n");
            else
                printf("No\n");
        }
    }
    return 0;
}

 


more is better

題目描述:

給定n個直接的朋友關係,求一個最大的集合

使這個集合裏的所有人都直接或間接的滿足朋友關係

求這個集合的大小。

Input

輸入n (0 ≤ n ≤ 100 000) - 直接的朋友關係. 接下來n行,每行輸入兩個數A,B,A與B互爲朋友(A ≠ B, 1 ≤ A, B ≤ 10000000)

 

Output

集合大小的最大值


案例:

Sample Input

4
1 2
3 4
5 6
1 6
4
1 2
3 4
5 6
7 8

Sample Output

4
2

 


題意:

跟上面的朋友圈問題類似,求出最大朋友圈然後在求出這個最大朋友圈元素的個數,A和B是朋友,B和C是朋友則A和C也是朋友,依次類推。這裏有個地方得注意,在找父節點時需要狀態壓縮,不然會超時


AC代碼:

#include<cstdio>
#include<iostream>

using namespace std;
const int maxn = 100001;
int fri[maxn];
int sum[maxn];
int res;

int find_fri(int n)
{
    if(fri[n] == n)
        return n;
    return fri[n] = find_fri(fri[n]);    //return find_fri(fri[n]);會出現超時,因此需要狀態跟着改變
}
void merge_ab(int a,int b)
{
    int x = find_fri(a);
    int y = find_fri(b);
    if(x != y) {
        fri[x] = y;
        sum[y] += sum[x];
        res = max(res,sum[y]);
    }
}
int main()
{
    int n;
    while(~scanf("%d",&n))
    {
        for(int i = 0; i <= maxn; i++) {
            fri[i] = i;
            sum[i] = 1;
        }
        int a,b;
        res = 1;
        for(int i = 0; i < n; i++) {
            scanf("%d%d",&a,&b);
            merge_ab(a,b);
        }
        printf("%d\n",res);
    }
    return 0;
}

 

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