# Prim's Algorithm# Greedy# Graph

f678 - FJCU_109_Winter_Day3_Lab2 最小生成樹練習

🔗 前往 ZeroJudge 原題

題目描述

題目要求給定一張包含 N 個頂點和 M 條邊的加權無向圖,計算其最小生成樹 (Minimum Spanning Tree, MST) 的總權重。

解題思路

本題採用 Prim's Algorithm 解決。Prim's Algorithm 是一種貪心演算法,用於在加權連通無向圖中找到最小生成樹。其基本思想是從一個起始頂點開始,逐步擴展生成樹,每次選擇與當前生成樹連接且權重最小的邊。

程式碼中,adj[i][j] 儲存頂點 i 到頂點 j 的權重,初始值為一個很大的數字 (1e9) 表示沒有邊。然後,根據輸入讀取邊的資訊,更新 adj 矩陣。d[i] 記錄從已建立的 MST 到頂點 i 的最小距離。visit[i] 記錄頂點 i 是否已經包含在 MST 中。

Prim's Algorithm 的主要步驟如下:

  1. 初始化 d[0] 為 0,其他頂點的 d[i] 為無限大。
  2. 迴圈 N 次,每次找到不在 MST 中且 d[i] 最小的頂點 a
  3. 將頂點 a 加入 MST,並更新與 a 相鄰的頂點的 d[i] 值。
  4. 迴圈結束後,d[i] 儲存了 MST 中每個頂點到 MST 的最小距離,將這些距離加總即可得到 MST 的總權重。

複雜度分析

  • 時間複雜度: O(N^2),其中 N 是頂點的數量。這是因為在每次迭代中,我們需要遍歷所有頂點來找到最小距離的頂點。
  • 空間複雜度: O(N^2),主要用於儲存鄰接矩陣 adj

程式碼

#include <iostream>  
using namespace std;
int n,m,ans,x,y,v,adj[101][101];  // adjacency matrix
int d[101];       // 記錄目前的MST到圖上各點的距離
bool visit[101];  // 記錄各個點是不是已在MST之中
void prim(){
    for (int i=0; i<n; i++) d[i] = 1e9;
    // 選擇任意一點做為樹根。此處選擇第零點做為樹根。
    d[0] = 0;
    for (int i=0; i<n; i++){
        // 找不在樹上、離樹最近的點。
        int a = -1, b = -1, min = 1e9;
        for (int j=0; j<n; j++)
            if (!visit[j] && d[j] < min){
                a = j;  // 持續記錄最近的點
                min = d[j];
            }
        // 從起點可連通的點已經找完
        if (a == -1) break;
        visit[a] = 1;
        // relaxation
        // 此處與Dijkstra's Algorithm不同,離樹最近,不是離根最近。
        for (b=0; b<n; b++)
            if  (!visit[b] && adj[a][b] < d[b])
                d[b] = adj[a][b];
    }
}
int main(){
	cin.tie(0); ios::sync_with_stdio(false);
    cin >> n >> m; 
    for(int i=0;i<n;++i)
    	for(int j=0;j<n;++j){
    		if(i==j)
    			adj[i][j]=0;
			else
				adj[i][j]=1e9;
		}
    for(int i=0;i<m;++i){
        cin >> x >> y >> v;
        adj[x][y]=min(adj[x][y],v);
        adj[y][x]=min(adj[y][x],v);
    }
    prim();
    for(int i=0;i<n;++i)
    	ans+=d[i];
    cout << ans << "\n";   
}

Discussion