給定 n 個點座標,部分點間已有高速公路(邊權為 0)。求最小生成樹,輸出尚需建設的邊(兩端點編號)。
稠密圖(n ≤ 750)適合 Prim O(n²) 實現。需記錄 pre[] 重構邊。
#include
#include
#include
#include
#define INF 0x3f3f3f3f
using namespace std;
int n, vis[755], pre[755], x[755], y[755], d[755], e[755][755];
void prim() {
memset(vis, 0, sizeof(vis));
for (int i = 2; i <= n; i++) {
pre[i] = 1;
d[i] = e[1][i];
}
d[1] = 0; vis[1] = 1;
for (int i = 2; i <= n; i++) {
int tmp = INF, p = 0;
for (int j = 1; j <= n; j++)
if (!vis[j] && tmp > d[j]) { tmp = d[j]; p = j; }
if (tmp != 0) printf("%d %d\n", pre[p], p); // 輸出新建邊
if (tmp == INF) break;
vis[p] = 1;
for (int j = 1; j <= n; j++)
if (!vis[j] && d[j] > e[p][j]) {
d[j] = e[p][j];
pre[j] = p;
}
}
}
int main() {
scanf("%d", &n);
for (int i = 1; i <= n; i++) scanf("%d%d", &x[i], &y[i]);
for (int i = 1; i <= n; i++)
for (int j = 1; j <= i; j++)
if (i == j) e[i][j] = 0;
else e[i][j] = e[j][i] = (x[i]-x[j])*(x[i]-x[j]) + (y[i]-y[j])*(y[i]-y[j]); // 距離平方
int q; scanf("%d", &q);
while (q--) {
int a, b; scanf("%d%d", &a, &b);
e[a][b] = e[b][a] = 0; // 已有邊權設 0
}
prim();
return 0;
}
sqrt,不影響大小比較。pre[] 記錄每點加入樹時的前驅點,構建輸出邊。tmp != 0 的邊,即非原有邊。時間 O(n²),空間 O(n²)(鄰接矩陣),n ≤ 750 完全滿足。
對於輸出邊的題目,Kruskal + 並查集同樣適用,代碼可能更簡潔(邊排序後依次加入)。