# 最小生成樹

# 生成樹

生成樹條件:

  1. 一棵包含圖上所有點的樹,稱作該圖的生成樹
  2. 一張圖的生成樹可能會有很多種
  3. 完全連通圖才有生成樹 (不連通時,則稱為生成森林)
  4. 生成樹的權重為樹上每條邊的權重總和

至於最小生成樹就是權重和最小的生成樹
概念上就是這樣:

# 作法

  1. 圖上每一個點,各自是一棵最小生成子樹 MSS。
  2. 圖上所有邊,依照權重大小,由小到大排序。
  3. 嘗試圖上所有邊,作為最小生成樹(森林)的邊:
    1. 兩端點分別位於兩棵 MSS,也就是產生了橋:
      1. 用這條邊連結兩棵 MSS,合併成一棵 MSS。
      2. 這條邊是最小生成樹(森林)上的邊。
    2. 兩端點皆位於同一棵 MSS,也就是產生了環:
      1. 捨棄這條邊。
        -> 所以需要使用到並查集喔~~

# 例題

# c125: 00534 - Frogger

給定一個無向圖,求最小生成樹

這是一題最小生成樹的模板題,刻個幾次就熟了

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
struct Tree{
    int s,e,w;
    bool operator<(Tree a)const{
        return a.w>w;
    }
};
int p[100000];
int Find(int a){
    if (p[a] == a) return a;
    else {
        p[a] = Find(p[a]);
        return p[a];
    }
}
void Kruskal(int n,int m) {
    Tree dot[m];
    for(int i=0;i<n;i++) p[i]=i;
    for (int i=0;i<m;i++)
        cin >>dot[i].s>>dot[i].e>>dot[i].w;
    sort(dot,dot+m);
    ll ans=0,cnt = 0;
    for (int i=0,x,y;i<m;i++) {
        x = Find(dot[i].e);
        y = Find(dot[i].s);
        if(x==y) continue;
        else {
            p[y] = x;
            ans +=dot[i].w;
            cnt++;
        }
    }
    if(cnt==n-1)cout<<ans;
    else cout<<-1;
    cout<<"\n";
}
int main() {
    int n,m;
    while (cin >>n>>m)
        Kruskal(n,m);
    return 0;
}

# b181: 2. 網路設計

給你一個字串形式的端點及路徑長,求出其的最小生成樹
第一行、輸出需要以字串形式輸出連結的狀況
第二行、輸出權重

難的部分就是如何處理其為字串的問題,一開始就將輸入的端點用兩個 map 對應:
字串對數字,數字對字串,每加入一個邊時就兩個端點加入 map 中,再開一個陣列紀錄圖形,最後再用使用 map 跟陣列做轉換就好了

#include<bits/stdc++.h>
using namespace std ;
struct str{
    int A,B,w,num;
    bool operator<(str a)const{
        return a.w>w|| (a.w==w && a.num>num);
    }
};
map<string,int> Map ;
map<int,string> reMap ;
int fa[100] ;
str E[100] ;
int Ans[100] ;
int root(int x){
    if (fa[x]==x)return x ;
    return root(fa[x]) ;
}
bool cmp(int a ,int b ){
    return E[a].A<E[b].A || (E[a].A==E[b].A && E[a].B<E[b].B) ;
}
int main(){
    int N ,M ;
    while (~scanf("%d%d",&N ,&M )){
        Map.clear() ;
        reMap.clear() ;
        //input -------------
        char A[30],B[30] ;
        for (int i=0 ;i<M ;i++ ){
            scanf("%s%s%d",&A ,&B ,&E[i].w ) ;
            if (Map[ (string)A ]==0){
                char c ;int in ;
                sscanf(A,"%c%d",&c ,&in ) ;
                Map[(string)A]=in ;
                reMap[in]=(string)A ;
            }
            E[i].A=Map[(string)A] ;
            if (Map[ (string)B ]==0){
                char c ;int in ;
                sscanf(B,"%c%d",&c ,&in) ;
                Map[(string)B]=in ;
                reMap[in]=(string)B ;
            }
            E[i].B=Map[(string)B] ;
            E[i].num=i ;
        }
        sort(E,E+M) ;
        //MST ---------------
        for (int i=1 ;i<=N ;i++ )fa[i]=i ;
        int Ansl=0 ,count=0 ;
        for (int i=0 ;i<M ;i++ ){
            int a=root( E[i].A ) ;
            int b=root( E[i].B ) ;
            if (a==b)continue ;
            fa[b]=a ;
            Ans[Ansl++]=i ;
            count+=E[i].w ;
        }
        //output ------------
        sort(Ans,Ans+Ansl,cmp) ;
        for (int i=0 ;i<Ansl ;i++ ){
            cout << "(" << reMap[ E[ Ans[i] ].A ] <<" "<< reMap[ E[ Ans[i] ].B ] <<") " ;
        }
        printf("\n%d\n",count) ;
    }
}
更新於 閱讀次數

用實際行動犒賞爆肝的我😀

Zrn Ye LinePay

LinePay