給定一個無向加權圖,部分邊已預先連接(權值視為 0),求連接所有村莊的最小總代價。標準最小生成樹(MST)問題,資料範圍較小(N ≤ 30),Kruskal 算法足以通過。
i < j 的邊加入列表。若兩村莊已有道路(輸入標記),將該邊權值設為 0,確保其優先被選入 MST。N-1 條邊或遍歷完畢#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <vector>
using namespace std;
const int MAXN = 32;
const int MAXE = 1000;
struct Edge {
int u, v, w;
bool operator<(const Edge& other) const { return w < other.w; }
};
int parent[MAXN];
Edge edges[MAXE];
int edgeCnt;
int findSet(int x) {
return parent[x] == x ? x : parent[x] = findSet(parent[x]);
}
bool unionSet(int a, int b) {
int ra = findSet(a), rb = findSet(b);
if (ra == rb) return false;
parent[ra] = rb;
return true;
}
int kruskal(int n) {
sort(edges, edges + edgeCnt);
for (int i = 1; i <= n; ++i) parent[i] = i;
int sum = 0, cnt = 0;
for (int i = 0; i < edgeCnt && cnt < n - 1; ++i) {
if (unionSet(edges[i].u, edges[i].v)) {
sum += edges[i].w;
++cnt;
}
}
return sum;
}
int main() {
int n;
while (scanf("%d", &n) == 1 && n) {
static int dist[MAXN][MAXN];
static bool connected[MAXN][MAXN];
memset(connected, 0, sizeof(connected));
for (int i = 1; i <= n; ++i)
for (int j = 1; j <= n; ++j)
scanf("%d", &dist[i][j]);
int m; scanf("%d", &m);
while (m--) {
int x, y; scanf("%d%d", &x, &y);
connected[x][y] = connected[y][x] = true;
}
edgeCnt = 0;
for (int i = 1; i <= n; ++i)
for (int j = i + 1; j <= n; ++j)
edges[edgeCnt++] = {i, j, connected[i][j] ? 0 : dist[i][j]};
printf("%d\n", kruskal(n));
}
return 0;
}