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;
}