1. 程式人生 > >演算法6-8~6-11:用樹表示的等價問題 (c語言)

演算法6-8~6-11:用樹表示的等價問題 (c語言)

題目描述

在離散數學中,對等價關係和等價類的定義是:

如果集合S中的關係R是自反的、對稱的和傳遞的,則稱它為一個等價關係。

等價關係是現實世界中廣泛存在的一種關係,許多應用問題可以歸結至等價類問題,這類問題通常被稱為等價問題。

通過使用集合,能夠解決等價問題。而集合可以通過雙親表示法的樹結構進行儲存。通過對樹結構的操作,可以實現查詢、歸併等操作。查詢操作和歸併操作的演算法如下:

在以上的歸併操作中,由於表示集合的樹的深度與樹形成的過程有關,因此在最壞情況下全部歸併操作將會有O(n2)的複雜度。而通過在歸併時比較子集所含成員的數目,令成員少的歸併至成員多的集合,將能夠提高演算法的效率。下面給出優化的歸併操作演算法:

另外,通過增加“壓縮路徑”的功能,即將所有從根到相應元素路徑上的元素都變成樹根的孩子。演算法如下所示:

本題中,將會給出n個原本互不相交的集合及k次集合合併的操作。通過這k次合併,判斷最終的某兩個原始的集合是否被合併成了同一個集合。

輸入描述

輸入的第一行包含兩個用空格隔開的正整數n和k,其中n不超過100,k不超過n-1。

之後的k行中,每行包含兩個用空格隔開的正整數x和y,表示將x元素所在的集合和y元素所在的集合合併至同一個集合。保證x和y均在1至n之間。

最後一行中,包含兩個正整數,表示需要判斷是否在同一個集合的元素編號。

輸出描述

共一行,包含字串“YES”或“NO”,“YES”表示需判斷的元素在同一個集合中,“NO”表示不在同一個集合中。請注意不需要輸出引號,且行尾輸出換行。

輸入樣例
5 2
1 3
2 3
1 2

輸出樣例
YES

/*這題就是將每個元素的集合的根節點設為自己,然後在將題目給出的元素根節點進行合併,最後找題目要求的兩個元素集合根節點是否相同
#include<stdio.h>
#include<string.h>
#include<stdlib.h>

int t[105];

int find(int a) //並查集查詢根節點,並且吧路徑上所有結點的根節點都變為同一個
{
    int r = a;
    while(t[r] != r)
    {
        r = t[r];
    }
    int i = a, j;
    while(i != r)
    {
        j = t[i];
        t[i]= r;
        i = j;
    }
    return r;
}

void Union(int a, int b) //根節點合併
{
    int x = find(a);
    int y = find(b);
    if(a != b)
    {
        t[a] = b;
    }
}

int main()
{
    int n, m, i, j, a, b, x, y;
    while(~scanf("%d %d", &n, &m))
    {
        for(i = 1; i <= n; i++)
        {
            t[i] = i;
        }
        for(i = 0; i < m ; i++)
        {
            scanf("%d %d", &a, &b);
            Union(a, b);
        }
        scanf("%d %d", &x, &y);
        if(find(x) != find(y))
        {
            printf("NO\n");
        }
        else
        {
            printf("YES\n");
        }
    }
    return 0;
}