給定 n (≤ 750) 個點的平面座標,部分點對已有公路相連(權重視為 0)。求仍需建設的公路集合,使所有點連通且總長度最小(輸出需新建邊的端點)。
O(E log E),E = n(n-1)/2 ≈ 280k,排序常數較大;需額外儲存所有邊。O(n²),n=750 → 562k 運算,無需額外邊表,極適合稠密圖;且易於記錄前驅節點輸出樹邊。本題採用 Prim + 前驅陣列 實作。
int n; // 點數 int x[MAXN], y[MAXN]; // 座標 int dist[MAXN][MAXN]; // 鄰接矩陣(平方距離,避免 sqrt) int minDist[MAXN]; // 非樹頂點到樹的最短距離 int parent[MAXN]; // 前驅節點,用於輸出樹邊 bool inMST[MAXN]; // 是否已加入生成樹
(dx*dx + dy*dy) 填入 dist(對稱)。q 條既有公路,將對應 dist[u][v] = dist[v][u] = 0。minDist[1]=0,其餘 INF;parent[i]=1。n 次:在未入樹頂點中找 minDist 最小者 u,加入樹。minDist[u] > 0(非預連邊),輸出 parent[u] u。v,若 dist[u][v] < minDist[v] 更新 minDist[v] 與 parent[v]。#include <stdio.h>
#include <string.h>
#include <limits.h>
#define MAXN 755
#define INF 0x3f3f3f3f
int n;
int x[MAXN], y[MAXN];
int dist[MAXN][MAXN];
int minDist[MAXN], parent[MAXN];
bool inMST[MAXN];
int main(void) {
while (scanf("%d", &n) == 1) {
for (int i = 1; i <= n; ++i) scanf("%d %d", &x[i], &y[i]);
/* 建立鄰接矩陣:平方距離 */
for (int i = 1; i <= n; ++i) {
dist[i][i] = 0;
for (int j = 1; j < i; ++j) {
int dx = x[i] - x[j];
int dy = y[i] - y[j];
dist[i][j] = dist[j][i] = dx*dx + dy*dy;
}
}
int q; scanf("%d", &q);
while (q--) {
int u, v; scanf("%d %d", &u, &v);
dist[u][v] = dist[v][u] = 0; /* 預連邊權重 0 */
}
/* Prim */
memset(inMST, 0, sizeof(inMST));
for (int i = 1; i <= n; ++i) {
minDist[i] = INF;
parent[i] = 1;
}
minDist[1] = 0;
for (int iter = 1; iter <= n; ++iter) {
int u = -1, best = INF;
for (int i = 1; i <= n; ++i)
if (!inMST[i] && minDist[i] < best) {
best = minDist[i];
u = i;
}
if (u == -1) break; // 圖不連通(題目保證連通)
inMST[u] = true;
if (minDist[u] > 0) // 非預連邊才輸出
printf("%d %d\n", parent[u], u);
for (int v = 1; v <= n; ++v)
if (!inMST[v] && dist[u][v] < minDist[v]) {
minDist[v] = dist[u][v];
parent[v] = u;
}
}
}
return 0;
}
O(n²) ≈ 56 萬次迴圈,極易在 1 秒內通過。dist[755][755] ≈ 2.3 MB,符合 64 MB 限制。int,建議用 long long 或 double 存儲距離。while(scanf(...)) 處理。n 較大(>2000),可改用二元堆 O(E log V) 或 Fibonacci 堆 O(E + V log V)。稠密圖 MST 首選 Prim + 鄰接矩陣,預連邊權重置 0 即可無縫融合。記錄 parent 陣列輕鬆輸出樹邊,是競程與工程中通用的技巧。