並查集入門題集

暢通工程

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

INPUT

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

  • 注意:兩個城市之間可以有多條道路相通,也就是說
    3 3
    1 2
    1 2
    2 1

  • 這種輸入也是合法的

  • 當N爲0時,輸入結束,該用例不被處理。 

OUTPUT

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

測試樣例

4 2
1 3
4 3
3 3
1 2
1 3
2 3
5 2
1 2
3 5
999 0
0

樣例輸出

1
0
2
998

題意理解:

  • 有n個城市,這n個城市由m條路相連。
  • 給出每條路連接的兩個城市。
  • 求還需要幾條路纔可以將全部的城市連接起來。

解題思路:

這道題是並查集的模板題,有人說這是一道生成樹,不過我覺得並查集理解起來好理解一些。

就是將已經聯通的城市歸爲一個集合,取出一個城市作爲根節點,輸入讀完之後就已經將聯通的城市歸爲一類了。只要查出有幾類就可以了,換句話說只要查出有幾個根就可以了,根的特點是他的前驅是它本身。

代碼:

#include<iostream>
#include<stdio.h>
using namespace std;
int cnt = 0;
int pre[1005];

int findpre(int x)        //尋找前驅
{
    int r = x;
    while(pre[r]!=r)
        r = pre[r];
    return r;
}
int join(int x,int y)     //合併節點
{
    int fx = findpre(x);  
    int fy = findpre(y);  
    if(fx!=fy)
    {
        pre[fx] = fy;
    }
    return 0;
}

int pl(int x)             //壓縮路徑,實際上這道題數據量比較小不需要路徑壓縮
{
    int r = x;
    while(pre[r]!=r)
        r = pre[r];
    int k = x;
    int j;
    while(k!=r)
    {
        j = pre[k];
        pre[k] = r;
        k = j;
    }
    return 0;
}

int main()
{
    while(1)
    {
        int n,m;
        scanf("%d",&n);
        if(n == 0)
            break;
        scanf("%d",&m);
        for(int i=0;i<=n;i++)
        {
            pre[i] = i;
        }
        for(int i=0;i<m;i++)
        {
            int f,t;
            scanf("%d%d",&f,&t);
            join(f,t);
        }
        for(int i=0;i<=n;i++)
        {
            pl(i);
        }
        for(int i = 1;i<=n;i++)
        {
            if(pre[i]==i)
            {
                cnt++;
            }
        }
        cout<<cnt-1<<endl;
        cnt = 0;

    }
    return 0;
}

How Many Tables

Today is Ignatius' birthday. He invites a lot of friends. Now it's dinner time. Ignatius wants to know how many tables he needs at least. You have to notice that not all the friends know each other, and all the friends do not want to stay with strangers.

One important rule for this problem is that if I tell you A knows B, and B knows C, that means A, B, C know each other, so they can stay in one table.

For example: If I tell you A knows B, B knows C, and D knows E, so A, B, C can stay in one table, and D, E have to stay in the other one. So Ignatius needs 2 tables at least.

INPUT

  • The input starts with an integer T(1<=T<=25) which indicate the number of test cases. Then T test cases follow.
  • Each test case starts with two integers N and M(1<=N,M<=1000). N indicates the number of friends, the friends are marked from 1 to N. Then M lines follow. Each line consists of two integers A and B(A!=B), that means friend A and friend B know each other. There will be a blank line between two cases.

OUTPUT

  • For each test case, just output how many tables Ignatius needs at least. Do NOT print any blanks.

測試樣例

2
5 3
1 2
2 3
4 5

5 1
2 5

樣例輸出

2
4

題意理解:

  • 和暢通工程那道題基本一樣,只不過這次說的是幾個人在一起dinner,認識的人可以坐到一張桌子上,輸入有幾個人,誰和誰互相認識,需要求要幾張桌子。

解題思路:

也是一道並查集模板題。

在每個認識的人裏選出一個代表作爲整個集合的根節點,最後查找幾個根就有幾張桌子。

不多說直接粘代碼了。

代碼:

#include<iostream>

using namespace std;
int pre[1010];
int findpre(int x)
{
    int r = x;
    while(r!=pre[r])
        r = pre[r];
    return r;
}
int join(int x,int y)
{
    int fx = findpre(x);
    int fy = findpre(y);
    if(fx!=fy)
    {
        pre[fx] = fy;
    }
    return 0;
}
int main()
{
    int t;
    cin>>t;
    for(int i=0;i<t;i++)
    {
        int m,n;
        cin>>m>>n;
        for(int j=1;j<=m;j++)
        {
            pre[j] = j;
        }
        for(int j=0;j<n;j++)
        {
            int f,t;
            cin>>f>>t;
            if(f>t)
                join(f,t);
            else
                join(t,f);
        }
        int cnt = 0;
        for(int j=1;j<=m;j++)
        {
            if(pre[j] == j)
            {
                //cout<<pre[j]<<endl;
                cnt++;
            }

        }
        cout<<cnt<<endl;
    }
    return 0;
}


PID331 / 家族

若某個家族人員過於龐大,要判斷兩個是否是親戚,確實還很不容易,現在給出某個親戚關係圖,求任意給出的兩個人是否具有親戚關係。

規定: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,表示Ai和Bi具有親戚關係。

  • 接下來p行:每行兩個數Pi,Pj,詢問Pi和Pj是否具有親戚關係。

OUTPUT

  • P行,每行一個’Yes’或’No’。表示第i個詢問的答案爲“具有”或“不具有”親戚關係。

測試樣例

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

樣例輸出

Yes
Yes
No

題意理解:

  • 給定幾個人和幾組關係,詢問幾組數據看是否有關係。

解題思路:

這道題還是一道板子題,直接用並查集就可以求解。解題思路和上面兩個題基本一致。

代碼:

#include<iostream>

using namespace std;
int pre[5005];
int findpre(int x)
{
    int r = x;
    while(pre[r]!=r)
        r = pre[r];
    return r;
}

int join(int x,int y)
{
    int fx = findpre(x);
    int fy = findpre(y);
    if(fx!=fy)
    {
        pre[fy] = fx;
    }
}

int main()
{
    int n,m,p;
    cin>>n>>m>>p;
    for(int i=1;i<=n;i++)
    {
        pre[i] = i;
    }
    for(int i=0;i<m;i++)
    {
        int x,y;
        cin>>x>>y;
        if(x<y)
            join(x,y);
        else
            join(y,x);
    }
    for(int i=0;i<p;i++)
    {
        int x,y;
        cin>>x>>y;
        if(findpre(x)==findpre(y))
        {
            cout<<"Yes"<<endl;
        }
        else
            cout<<"No"<<endl;
    }

}

 

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