POJ 2524 Ubiquitous Religions(並查集)

Description

There are so many different religions in the world today that it is difficult to keep track of them all. You are interested in finding out how many different religions students in your university believe in. 

You know that there are n students in your university (0 < n <= 50000). It is infeasible for you to ask every student their religious beliefs. Furthermore, many students are not comfortable expressing their beliefs. One way to avoid these problems is to ask m (0 <= m <= n(n-1)/2) pairs of students and ask them whether they believe in the same religion (e.g. they may know if they both attend the same church). From this data, you may not know what each person believes in, but you can get an idea of the upper bound of how many different religions can be possibly represented on campus. You may assume that each student subscribes to at most one religion.

Input

The input consists of a number of cases. Each case starts with a line specifying the integers n and m. The next m lines each consists of two integers i and j, specifying that students i and j believe in the same religion. The students are numbered 1 to n. The end of input is specified by a line in which n = m = 0.

Output

For each test case, print on a single line the case number (starting with 1) followed by the maximum number of different religions that the students in the university believe in.

Sample Input

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

Sample Output

Case 1: 1

Case 2: 7

題目大意:

有n個人,m對人有相同的宗教信仰,求最多有幾種宗教信仰。

解題思路:

將相同宗教信仰的人合併,用到並查集的知識。

將近一個月沒敲代碼了TT。知識點都忘得差不多了。只好刷刷水題來練練手了。看完題就知道用並查集做了。但是代碼敲不出來(太弱了),可能是之前學並查集也沒親自敲擊此代碼。順便複習一下:

並查集支持以下三種操作:

1、Make_Set(x) 把每一個元素初始化爲一個集合

初始化後每一個元素的父親節點是它本身,每一個元素的祖先節點也是它本身。

2、Find_Set(x) 查找一個元素所在的集合

查找一個元素所在的集合,其精髓是找到這個元素所在集合的祖先!這個纔是並查集判斷和合並的最終依據。

判斷兩個元素是否屬於同一集合,只要看他們所在集合的祖先是否相同即可。 合併兩個集合,也是使一個集合的祖先成爲另一個集合的祖先,具體見示意圖

3、Union(x,y) 合併x,y所在的兩個集合

合併兩個不相交集合操作很簡單: 利用Find_Set找到其中兩個集合的祖先,將一個集合的祖先指向另一個集合的祖先。

實現方法

1.用編號最小的元素標記所在集合;

2.定義一個數組 set[1..n] ,其中set[i] 表示元素i 所在的集合;

i: 1

2

3

4

5

6

7

8

9

10

Set[i]

  :               1

2

1

4

2

6

1

6

2

2

不相交集合:{1,3,7}, {4}, {2,5,9,10}, {6,8}

並查集的優化

1、Find_Set(x)時 路徑壓縮 尋找祖先時我們一般採用遞歸查找,但是當元素很多亦或是整棵樹變爲一條鏈時,每次Find_Set(x)都是O(n)的複雜度。路徑壓縮,即當我們經過"遞推"找到祖先節點後,"回溯"的時候順便將它的子孫節點都直接指向祖先,這樣以後再次Find_Set(x)時複雜度就變成O(1)了。

樸素查找的代碼,適合數據量不大的情況:

int findx(int x) {    

int r=x;   

while(parent[r] !=r)         r=parent[r];

return  r; }

下面是採用路徑壓縮的方法查找元素:

      int find(int x)  //查找x元素所在的集合,回溯時壓縮路徑

      {   

 if (x != parent[x])        {               parent[x] = find(parent[x]);     //回溯時的壓縮路徑   

         } //x結點搜索到祖先結點所經過的結點都指向該祖先結點              return  parent[x];                }

上面是一採用遞歸的方式壓縮路徑, 但是,遞歸壓縮路徑可能會造成溢出棧,下面我們說一下非遞歸方式進行的路徑壓縮:

int find(int x)

{    

int k, j, r;    

r = x;    

while(r != parent[r])     //查找跟節點        

r = parent[r];      //找到跟節點,用r記錄下   

 k = x;           

 while(k !=r)             //非遞歸路徑壓縮操作  

 {      

  j = parent[k];         //j暫存parent[k]的父節點      

  parent[k] = r;        //parent[x]指向跟節點       

  k = j;                    //k移到父節點   

        }    

return  r;         //返回根節點的值        

}

附上代碼:

#include<iostream>
#include<stdlib.h>
#include<stdio.h>
#include<string.h>
#include<algorithm>
using namespace std;
#define M 50001
int pre[M];
int t[M];
void makeset(int n)
{
    int i;
    for(i=1;i<=n;i++)
        pre[i]=i;
}
int find(int x)
{
    int r=x;
    while(r!=pre[r])
        r=pre[r];
    int i=x,j;
    while(pre[i]!=r)
    {
        j=pre[i];
        pre[i]=r;
        i=j;
    }
    return r;
}
void mix(int p,int q)
{
    int x=find(p),y=find(q);
    if(x!=y)
    {
        pre[y]=x;
    }
}
int main()
{
    int n,m,i,p,q;
    int ans=0;
    while(scanf("%d%d",&n,&m)!=EOF&&n&&m)
    {
        ans++;
        int count =0;
        makeset(n);
        for(i=1;i<=m;i++)
        {
            scanf("%d%d",&p,&q);
            mix(p,q);
        }
        memset(t,0,sizeof(t));
        for(i=1;i<=n;i++)
        {
            t[find(i)]=1;
        }
        for(i=1;i<=n;i++)
        {
            if(t[i])
                count++;
        }
        printf("Case %d: %d\n",ans,count);
    }
    return 0;
}


推薦做HDU 1232 暢通工程點擊打開鏈接

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