Kruskal’s Algorithm

Kruskal’s Algorithm 是用來找最小生成樹的演算法
其尋找的方式是從不斷添加權重最小的邊,且如果添加後形成環就將其移除

在實作上,可以用 Union-Find 判斷會不會形成環

要點

需要一個一維陣列,裡面放邊權重、A 節點、B 節點
將其排序後遍歷,並且透過 Union-Find 檢查
如果兩者的祖先不相同則代表添加後不會形成環
添加後將兩節點 合併(Union)

實作

這邊是 Kruskal’s Algorithm 會用到的 Union-Find
詳細的解釋在 這裡

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
vector<int> anc;

int Find(int x) {
    return (anc[x] == x ? x : anc[x] = Find(anc[x]));
}

bool Union(int a, int b) {
    anc[Find(b)] = Find(a);
}

int main() {
    int n;
    cin >> n;
    anc.resize(n);
    iota(anc.begin() , anc.end(), 0);
}

將邊的權重放在第一項,節點編號放在後面
並且透過 sort 依照邊的權重排序

1
2
3
vector<tuple<int, int, int>> g;
g.push_back({w, a, b});
sort(g.begin(), g.end());

之後遍歷該陣列,檢查 Find(a) != Find(b)
若條件成立,則添加此邊

1
2
3
4
5
6
for (auto [w, a, b] : g) {
    if (Find(a) != Find(b)) {
        ...... //依照問題需求動作
        Union(a, b);
    }
}

範例

題目:UVA 10034

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
#include <bits/stdc++.h>
using namespace std;

#define pb push_back
#define fi first
#define se second
#define INF 0x3f3f3f3f
#define WA() ios_base::sync_with_stdio(false); cin.tie(NULL); cout.tie(NULL)
#define all(x) (x).begin(), (x).end()
#define sz(x) ((int)(x).size())

vector<int> anc;


int Find(int x) {
    return (anc[x] == x ? x : anc[x] = Find(anc[x]));
}

bool Union(int a, int b) {
    a = Find(a), b = Find(b);
    if (a == b) return false;
    return anc[b] = a, true;
}

signed main() { WA();
    int t;
    cin >> t;
    while (t--) {
        int n;
        cin >> n;
        anc.resize(n);
        iota(all(anc), 0);
        vector<pair<double, double>> v(n);
        vector<tuple<double, double, double>> g;
        for (auto &i : v) cin >> i.fi >> i.se;
        for (int i = 0; i+1 < n; i++) for (int j = i + 1; j < n; j++) {
                g.push_back({sqrt((v[j].fi-v[i].fi)*(v[j].fi-v[i].fi)+(v[j].se-v[i].se)*(v[j].se-v[i].se)), i, j});
        }
        sort(all(g));

        double ans = 0;
        for (auto &[dis, a, b] : g) if (Union(a, b)) ans += dis;

        cout << fixed << setprecision(2) << ans << '\n';
        if (t) cout << '\n';
    }
}

題目:UVA 1208

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
// 我不會處理這題測資的輸入 :(
#include <bits/stdc++.h>
using namespace std;

#define pb push_back
#define fi first
#define se second
#define INF 0x3f3f3f3f
#define WA() ios_base::sync_with_stdio(false); cin.tie(NULL); cout.tie(NULL)
#define all(x) (x).begin(), (x).end()
#define sz(x) ((int)(x).size())
#define int long long

vector<int> anc;

int Find(int x) {
    return (anc[x] == x ? x : anc[x] = Find(anc[x]));
}

bool Union(int a, int b) {
    a = Find(a), b = Find(b);
    if (a == b) return false;
    return anc[b] = a, true;
}

signed main() { WA();
    int t;
    cin >> t;
    for (int c = 1; c <= t; c++) {
        int n;
        cin >> n;
        cin.ignore();
        vector<vector<int>> v(n);
        for (int i = 0; i < n; i++) {
            string s;
            int tmp;
            getline(cin, s);
            for (auto &c : s) if (!isalnum(c)) c = ' ';
            stringstream ss(s);
            while (ss >> tmp) v[i].push_back(tmp); 
        }

        vector<tuple<int, int, int>> g;
        for (int i = 0; i+1 < n; i++) for (int j = i+1; j < n; j++) {
            if (v[i][j]) g.push_back({v[i][j], i, j});
        }

        sort(all(g));

        anc.resize(n);
        iota(all(anc), 0);
        cout << "Case " << c << ":\n";
        for (auto &[w, a, b] : g) if (Union(a, b)) printf("%c-%c %d\n", a+'A', b+'B', w);
    }
}