---
# System prepended metadata

title: 最小生成樹mst

---

# 最小生成樹mst

* n個點，剛好有n-1條邊的就是樹（無向）
* 求一個有權重的圖中權重最小的樹的路徑
* 皆可以有負權重
* 也可以算最大生成樹
* 邊較多的圖：prim, 邊較少的圖：kruskal

prim
---

* 設s為root, $dis[v]$代表s到v的最點距離，$weight[a][b]$為a到b的權重，$done[v]=1$代表點v已走過, $parent[v]$是v的父節點
    * $dis[s]=0$，其餘為無限大
    * done設為0
* **步驟：**
    1. 從沒走過的點中，找一個點x其$dis[x]$最小的，設done[x]=1，所以一開始x就是起點s
        * 用priority_queue找dis[x]最小的，每次找最小又還沒走過的點，所以每次push{dis[u], u}，pop要檢查done[x]是否為1
    2. 對於每個x連到的點y（如果已經做過done[y]==1就跳過，因為不能有環），做$dis[y]=min(dis[y], weight[x][y])$
    3. 重複 1.
* $O(ElogV)$

```cpp=
#include <bits/stdc++.h>
#define EB emplace_back
#define vci vector<int>
#define PII pair<int,int>
#define F first
#define S second
#define rep(a,b) for(int i=a;i<b;++i)
#define ALL(a) (a).begin(), (a).end()
#define SZ(a) (int)(a).size()
#define NL "\n"
#define LL long long

using namespace std;

int main(){
    ios_base::sync_with_stdio(false);
    cin.tie(0);
    int n, m, u, v, w;
    cin>>n>>m;
    vector<vector<PII>> gh(n, vector<PII>());
    rep(0,m){
        cin>>u>>v>>w;
        gh[u].EB(make_pair(v, w));
        gh[v].EB(make_pair(u, w));
    //doesn't matter if there's multiple line between two nodes, the algorithm will take the least one
    }

    //init
    vector<int> done(n, 0);
    vector<int> parent(n);
    vector<int> dis(n, INT_MAX);
    dis[0]=0; //default dis[s] is 0
    
    priority_queue<PII, vector<PII>, greater<PII>> pq;
    pq.push({dis[0], 0});
    
    while(!pq.empty()){
        
        PII x=pq.top();
        pq.pop();
        if(done[x.S]) continue;
        done[x.S]=1;
        
        for(auto a:gh[x.S]){
            
            if(done[a.F]) continue; //need to do this
            if(a.S<dis[a.F]){
                dis[a.F]=a.S;
                parent[a.F]=x.S;
                pq.push({dis[a.F], a.F});
            }
            
        }
    }
    
    int sum=0, flag=0;
    rep(0,n){
        if(!done[i]){
            cout << -1 << NL;
            flag=1;
            break;
        }
        sum+=dis[i];
    }
    if(!flag) cout << sum << NL;
}
```

kruskal
---

1. 對各邊權重做排序
2. 從小到大選擇，如果不會形成迴圈就放進最小生成樹集合中，反之就忽略
    * 用並查集判斷是否為迴圈
3. 如果最後邊數不到n-1就不可能組成樹
* $O(ElogE)$ or $O(VlogV)$ ?

```cpp=
#include <bits/stdc++.h>
#define EB emplace_back
#define vci vector<int>
#define PII pair<int,int>
#define F first
#define S second
#define rep(X, a,b) for(int X=a;X<b;++X)
#define ALL(a) (a).begin(), (a).end()
#define SZ(a) (int)(a).size()
#define NL "\n"
#define LL long long

using namespace std;

int p[10010];

int fd(int x){
    if(p[x]==x) return x;
    return fd(p[x]);
}

bool un(int a, int b){
    int f1=fd(a), f2=fd(b);
    if(f1==f2) return false;
    p[f2]=f1;
    return true;
}

int main(){
    ios_base::sync_with_stdio(false);
    cin.tie(0);
    int n, m, u, v, w;
    cin>>n>>m;
    vector<pair<int, pair<int,int>>> gh(n, pair<int, pair<int,int>>());
    //pair: {weight, {node1, node2}}

    rep(i,0,m){
        cin>>u>>v>>w;
        gh.EB(make_pair(w, make_pair(u, v)));
    }
    sort(ALL(gh));

    //init
    rep(i, 0, n) p[i]=i;

    LL cost=0, edge=0;
    for(auto v:gh){
        if(un(v.S.F, v.S.S)){
            edge++;
            cost+=v.F;
        }
    }

    if(edge<n-1) cout << -1 << NL;
    else cout << cost << NL;
}
```