N 個村莊 (頂點),N(N-1)/2 條邊 (完全圖),邊權為距離。N-1 條邊連通所有頂點,邊權和最小 → **最小生成樹 (MST)**。N=0 結束。完全圖邊數 ~ O(N²),Prim + 鄰接矩陣 O(N²) 最優;Kruskal 需排序 O(E log E) = O(N² log N) 稍慢。
#include <stdio.h>
#include <limits.h>
#define MAXN 100
#define INF INT_MAX
int graph[MAXN][MAXN];
int dist[MAXN];
int visited[MAXN];
int prim(int n) {
for (int i = 0; i < n; i++) {
dist[i] = INF;
visited[i] = 0;
}
dist[0] = 0; // 從頂點 0 開始
int total = 0;
for (int iter = 0; iter < n; iter++) {
// 1. 選擇未訪問、距離最小的頂點 u
int u = -1, minDist = INF;
for (int i = 0; i < n; i++) {
if (!visited[i] && dist[i] < minDist) {
minDist = dist[i];
u = i;
}
}
if (u == -1) break; // 圖不連通 (按題意不會發生)
visited[u] = 1;
total += dist[u];
// 2. 更新鄰居距離
for (int v = 0; v < n; v++) {
if (!visited[v] && graph[u][v] < dist[v]) {
dist[v] = graph[u][v];
}
}
}
return total;
}
int main() {
int N;
while (scanf("%d", &N) == 1 && N != 0) {
// 初始化鄰接矩陣
for (int i = 0; i < N; i++)
for (int j = 0; j < N; j++)
graph[i][j] = (i == j) ? 0 : INF;
// 讀取 N(N-1)/2 條邊
int edges = N * (N - 1) / 2;
for (int k = 0; k < edges; k++) {
int u, v, w;
scanf("%d %d %d", &u, &v, &w);
u--; v--; // 題目編號 1~N → 數組 0~N-1
graph[u][v] = graph[v][u] = w;
}
printf("%d\n", prim(N));
}
return 0;
}
輸入:
3
1 2 1
1 3 2
2 3 4
4
1 2 1
1 3 4
1 4 1
2 3 3
2 4 2
3 4 5
0
輸出:
3
5
解析樣例 1:選邊 (1,2)=1, (1,3)=2,總權 3。原帖質疑「2-3 距離 4 但公路只需 3」正是 MST 核心——**不要求直接相連,只需通過其他村莊間接通達**。
struct Edge {int u, v, w;};
int parent[MAXN];
int find(int x) { return parent[x]==x ? x : (parent[x]=find(parent[x])); }
void unite(int a, int b) { parent[find(a)] = find(b); }
int kruskal(int n, struct Edge *edges, int m) {
for (int i = 0; i < n; i++) parent[i] = i;
// 按權重升序排序
qsort(edges, m, sizeof(struct Edge),
(int(*)(const void*,const void*))[](const void* a, const void* b) {
return ((struct Edge*)a)->w - ((struct Edge*)b)->w;
});
int total = 0, cnt = 0;
for (int i = 0; i < m && cnt < n-1; i++) {
if (find(edges[i].u) != find(edges[i].v)) {
unite(edges[i].u, edges[i].v);
total += edges[i].w;
cnt++;
}
}
return total;
}
| 算法 | 時間複雜度 | 空間複雜度 | 適用場景 |
|---|---|---|---|
| Prim (鄰接矩陣) | O(N²) | O(N²) | 稠密圖 (本題完全圖) |
| Prim (鄰接表 + 堆) | O(E log V) | O(E) | 稀疏圖 |
| Kruskal | O(E log E) | O(E) | 稀疏圖、邊列表輸入 |
u--; v--;。while (scanf("%d", &N) && N) 正確處理 N=0 終止。INT_MAX 或足夠大的常數 (如 0x3f3f3f3f),避免溢出。u==-1 或 cnt。 「暢通工程」是經典**最小生成樹**問題。對完全圖輸入,**Prim 算法配合鄰接矩陣** 代碼最簡、效率最高 (O(N²))。關鍵點在於正確讀取邊構建矩陣、下標轉換、多用例循環。掌握 Prim/Kruskal 雙算法,可靈活應對稠密/稀疏圖各類 MST 變體題。