HDU 1325 1272 並查集判斷有向圖和無向圖是否構成一棵樹型

HDU 1325
HDU 1272

HDU 1325是有向圖,給定某個節點是另一個元素的父節點
HDU 1272是無向圖,將兩個節點連接起來

共同點:判斷是否成環,判斷是樹木還是森林

區別:有向圖可能出現多個箭頭指向同一個節點的情況(即一個節點多個父節點)
例如
多節點指向同一節點

另外,關於HDU 1272

評論區看到一種利用離散數學結論的解法
(對於無向圖)
如果m個節點被連成環,那麼邊的條數就是 m
如果m個節點形成了n個樹,那麼邊的條數就是 m - n
(這個動動筆畫畫就能明白)
鏈接:這題目幹嘛都用並查集做啊
那麼按照題目要求:Yes的條件就是
1.邊的條數 = 所有節點數 - 1

2.節點數爲0

上代碼:
HDU 1325

#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>

#define maxlen 200005

using namespace std;

//並查集實現

int roots, ok;

int fa[maxlen];

// 這個棧和棧頂指針只是用來記錄出現過的元素,用來清空fa數組用的,
// 記錄fa數組的哪些地方被用過了,
// 完成一個示例以後就根據這個棧裏面記錄的位置來把fa數組裏面對應的位置恢復成0
// 之所以不用memset,是因爲fa數組很大,不是所有部分都用上了,每次都把整個數組寫0太浪費時間了
int stack[maxlen];
int top = -1;

int find(int i) {//非遞歸實現
    int icopy = i;
    while (i != fa[i]) {//找到根元素
        i = fa[i];
    }

    while (icopy != fa[icopy]) {
        icopy = fa[icopy];//獲取父節點
        fa[icopy] = i;//掛到根節點下
    }

    return i;
}

int main(int argc, char const *argv[]) {

    int a, b, t = 1;

    int count = 0;
    while (1) {

        ok = 1;
        count = 0;

        // memset(fa, 0, sizeof(fa));//每次都全部寫0,耗時長,改爲用棧記錄修改過的位置

        while (scanf("%d%d", &a, &b) && a > 0 && b > 0) {

            // a --> b
            // a是b的父節點

            if (ok) {
                if (!fa[a]) { //若a沒有出現過,就初始化爲自己
                    fa[a] = a;
                    count++;

                    top++;
                    stack[top] = a;
                    // printf("add\n");
                }
                if (!fa[b]) {
                    fa[b] = b;

                    top++;
                    stack[top] = b;

                } else {
                    if (fa[b] == b) {
                        count--;
                        // printf("sub\n");
                    }
                }

                if (fa[b] != b && fa[b] != a) {//若出現多指一,則不ok
                    ok = 0;
                    // printf("die 1\n");
                    continue;
                } else {
                    a = find(a);//把a換爲a的根節點
                    if (b == a) {//環
                        ok = 0;
                        // printf("die 2\n");
                        continue;
                    } else {
                        fa[b] = a;//直接掛在根節點
                    }
                }
            }
        }

        if (a < 0 && b < 0) {
            break;
        }
        if (ok) {
            if (count != 1) {
                ok = 0;
                // printf("die 3\n");
            }
        }

        printf("Case %d is %sa tree.\n", t, ok ? "" : "not ");

        while (top != -1) {
            fa[stack[top]] = 0;
            top--;
        }

        t++;
    }

    return 0;
}

HDU 1272

#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>

#define maxlen 200005

using namespace std;

//並查集實現
//雖然給的輸入似乎是無向的,但最終我們構造的依然是一個有向的樹,我們只要考慮是否會出現環和森林,所以這題和hdu1325性質相同

int ok;

int fa[maxlen];

// 這個棧和棧頂指針只是用來記錄出現過的元素,用來清空fa數組用的,
// 記錄fa數組的哪些地方被用過了,
// 完成一個示例以後就根據這個棧裏面記錄的位置來把fa數組裏面對應的位置恢復成0
// 之所以不用memset,是因爲fa數組很大,不是所有部分都用上了,每次都把整個數組寫0太浪費時間了
int stack[maxlen];
int top = -1;

int find(int i) {//非遞歸實現
    int icopy = i;
    while (i != fa[i]) {//找到根元素
        i = fa[i];
    }

    while (icopy != fa[icopy]) {
        icopy = fa[icopy];//獲取父節點
        fa[icopy] = i;//掛到根節點下
    }

    return i;
}

int main(int argc, char const *argv[]) {

    int a, b;

    int countOfRoots = 0;
    while (1) {

        ok = 1;
        countOfRoots = 0;

        // memset(fa, 0, sizeof(fa));//每次都全部寫0,耗時長,改爲用棧記錄修改過的位置

        while (scanf("%d%d", &a, &b) && a > 0 && b > 0) {

            if (ok) {

                if (!fa[a]) { //若a沒有出現過,就初始化爲自己

                    top++;
                    stack[top] = a;//所有出現過的節點都記錄在stack中

                    if (!fa[b]) {//若b也沒有出現過

                        top++;
                        stack[top] = b;

                        //指定a爲b的父節點,a爲獨立的根節點

                        fa[a] = a;
                        fa[b] = a;
                        countOfRoots++;//根節點數量+1

                    } else {//若b出現過

                        fa[a] = find(b);//把a直接掛到b的根節點下

                    }

                } else {//若a出現過

                    if (!fa[b]) {//而b沒出現過

                        top++;
                        stack[top] = b;

                        fa[b] = find(a);//把b直接掛到a的根節點下
                    } else {//b也出現過

                        if (find(a) == find(b)) {//同一根節點
                            //成環
                            ok = 0;
                        } else {
                            // 把兩個樹合併
                            fa[find(a)] = find(b);
                            countOfRoots--;
                        }
                    }
                }
            }
        }

        if (a < 0 && b < 0) {
            break;
        }

        if (ok) {
            if (top != -1) {//排除掉一個元素都沒有的情況(空樹)
                if (countOfRoots != 1) {
                    ok = 0;
                }
            }
        }

        printf("%s\n", ok ? "Yes" : "No");

        while (top != -1) {
            fa[stack[top]] = 0;
            top--;
        }
    }

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