N≤50 城市,邊權 a[i][j](-1 表示無邊),過路費 b[i]。多組查詢 c→d,求最小總費用(路徑邊權和 + 經過中間城市過路費和),若多解輸出字典序最小路徑。
松弛條件:
dis[i][j] = min(dis[i][j], dis[i][k] + dis[k][j] + cost[k])
其中 cost[k] 為城市 k 的過路費。源點和終點不收費,故 Floyd 過程中 k 作為中介點時加費,完美建模。
path[i][j] = 從 i 到 j 的第一步(後繼)。初始 path[i][j]=j。
鬆弛時:
if (tmp < dis[i][j] || (tmp == dis[i][j] && path[i][j] > path[i][k])) {
dis[i][j] = tmp;
path[i][j] = path[i][k]; // 經 k 的第一步 = i→k 的第一步
}
打印路徑:
u = st; while (u != en) { print(u); u = path[u][en]; } print(en);
從前向後打印,天然字典序最小(因優先選擇最小後繼)。
void floyd() {
for (k=1..n) for (i=1..n) for (j=1..n)
if (dis[i][k]+dis[k][j]+cost[k] < dis[i][j] ||
(== && path[i][j] > path[i][k])) {
dis[i][j] = ...; path[i][j] = path[i][k];
}
}
void solve() {
while (scanf("%d%d",&st,&en), st!=-1) {
printf("From %d to %d :\nPath: %d", st,en,st);
for (int u=st; u!=en; u=path[u][en]) printf("-->%d", u);
printf("\nTotal cost : %d\n\n", dis[st][en]);
}
}
path[i][j] 存後繼,打印無需遞歸/棧,直接迭代path[i][j] > path[i][k] 時更新,保證第一步最小N=50,Floyd O(N^3)=125,000 極快