1. 程式人生 > 實用技巧 >L2-024 部落-middle-並查集-set-(超時原因)

L2-024 部落-middle-並查集-set-(超時原因)

在一個社群裡,每個人都有自己的小圈子,還可能同時屬於很多不同的朋友圈。我們認為朋友的朋友都算在一個部落裡,於是要請你統計一下,在一個給定社群中,到底有多少個互不相交的部落?並且檢查任意兩個人是否屬於同一個部落。

輸入格式:

輸入在第一行給出一個正整數N(≤104),是已知小圈子的個數。隨後N行,每行按下列格式給出一個小圈子裡的人:

K P[1] P[2] ⋯ P[K]

其中K是小圈子裡的人數,P[i](i=1,⋯,K)是小圈子裡每個人的編號。這裡所有人的編號從1開始連續編號,最大編號不會超過104。

之後一行給出一個非負整數Q(≤104),是查詢次數。隨後Q行,每行給出一對被查詢的人的編號。

輸出格式:

首先在一行中輸出這個社群的總人數、以及互不相交的部落的個數。隨後對每一次查詢,如果他們屬於同一個部落,則在一行中輸出Y,否則輸出N

輸入樣例:

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

輸出樣例:

10 2
Y
N

超時原因 並查集沒有路徑壓縮

#include<iostream>
#include<set>
#define MAXSIZE 10002
using namespace std;
int N, K, pre[MAXSIZE] = { 0 };
set<int> s;
int find(int x) {
    int root = x;
    while (pre[root] != root) {
        root = pre[root];
    }
      /*路徑壓縮*/
    /*x的根節點為root; : 每個節點直接連線根結點, 將x前面所有前導元素都連線到根節點上去*/
    int i = x, j;
    while (i != root) {
        j = pre[i];/*儲存當前i的前導*/
        pre[i] = root;/*當前i前導直接連線到根節點上去*/
        i = j;/*i=開始儲存的i的前導*/
    }

    return root;
}
void join(int a, int b) {
    if (find(a) != find(b))
        pre[find(a)] = b;
}
int main() {
    scanf("%d", &N);
    int p, t;
    for (int i = 0; i < N; ++i) {
        scanf("%d %d", &K, &p);
        if (pre[p] == 0)/*當沒有初始化的時候進行初始化*/
            pre[p] = p;
        s.insert(p);
        for (int j = 1; j < K; ++j) {
            scanf("%d", &t);
            if (pre[t] == 0)/*當沒有初始化的時候進行初始化*/
                pre[t] = t;
            s.insert(t);
            join(p, t);
        }
    }
/*利用set的erase 和 begin 函式 找到部落個數,即首領為自己的點的個數*/
    int num_people = s.size(), num_group = 0;
    while ( s.begin() != s.end() ) {
        if (*s.begin() == find(*s.begin()))
            ++num_group;
        s.erase(s.begin());
    }
    cout << num_people << " " << num_group << endl;

    //判斷是否在同一個部落裡
    int Q, a, b;
    scanf("%d", &Q);
    for (int i = 0; i < Q; ++i) {
        scanf("%d %d", &a, &b);
        printf("%c\n", find(a) == find(b) ? 'Y' : 'N');
    }
    return 0;
}