Vijos - 觀光旅遊(Floyd最短路)
題目描述
學校裡面有N個景點。兩個景點之間可能直接有道路相連,用Dist[I,J]表示它的長度;否則它們之間沒有直接的道路相連。這裡所說的道路是沒有規定方向的,也就是說,如果從I到J有直接的道路,那麼從J到I也有,並且長度與之相等。學校規定:每個遊客的旅遊線路只能是一個迴路(好霸道的規定)。也就是說,遊客可以任取一個景點出發,依次經過若干個景點,最終回到起點。一天,Xiaomengxian決定到湖南師大附中旅遊。由於他實在已經很累了,於是他決定儘量少走一些路。於是他想請你——一個優秀的程式設計師——幫他求出最優的路線。怎麼樣,不是很難吧?(摘自《鬱悶的出納員》)
輸入
對於每組資料:
第一行有兩個正整數N,M,分別表示學校的景點個數和有多少對景點之間直接有邊相連。(N<=100,M<=10000)
以下M行,每行三個正整數,分別表示一條道路的兩端的編號,以及這條道路的長度。
輸出
對於每組資料,輸出一行:
如果該回路存在,則輸出一個正整數,表示該回路的總長度;否則輸出“No solution.”(不要輸出引號)
樣例輸入
5 7
1 4 1
1 3 300
3 1 10
1 2 16
2 3 100
2 5 15
5 3 20
4 3
1 2 10
1 3 20
1 4 30
樣例輸出
61
No solution.
解題思路
因為n很小可以直接Floyd求最小環,注意最小環ans更新的位置,每次列舉i為下家更新點,應該先嚐試更新ans再更新j,k之間的最短路,我們用q.dis[j][k]來記錄i,j之間最短路徑,而p.dis[j][k]用來儲存j,k之間的原始路徑長度。
因為我們知道j,k要構成一個最小環,肯定要有兩條路可走:
1.直接從j到k。
2.從j經過某個中途點i到達k。
即對於每一個i我們先嚐試從這個i點對於所有的j,k點能不能得到最小環,然後我們再用這個i點嘗試更新路徑。
比普通Floyd多出來的部分,主要利用到的原理是當處理到i時,所有以0到k-2為中間結點的最短路徑都已經確定,則這時候的環為(j到k(0<j,k<i)的最短路徑)+邊(i,k)+邊(k,j),遍歷所有的j,k找到上述式子的最小值即為在i下的最小代價環。
#include <stdio.h>
const int inf = 99999999;
struct edge {
int dis[110][110];
}q, p;
int ans, n, m;
void Add(int u, int v, int w) {
p.dis[u][v] = w;
}
void Floyd()
{
for (int i = 0; i < n; i++)
{
for (int j = 0; j < i; j++)
for (int k = j + 1; k < i; k++)
if (q.dis[j][k] + p.dis[j][i] + p.dis[i][k] < ans)
ans = q.dis[j][k] + p.dis[j][i] + p.dis[i][k];
for (int j = 0; j < n; j++)
for (int k = 0; k < n; k++)
if (q.dis[j][k] > q.dis[j][i] + q.dis[i][k])
q.dis[j][k] = q.dis[j][i] + q.dis[i][k];
}
}
int main()
{
int u, v, w;
while (~scanf("%d%d", &n, &m))
{
ans = inf;
for (int i = 0; i < n; i++)
for (int j = 0; j < n; j++)
p.dis[i][j] = inf;
for (int i = 0; i < m; i++)
{
scanf("%d%d%d", &u, &v, &w);
Add(u - 1, v - 1, w);
Add(v - 1, u - 1, w);
}
q = p;
Floyd();
if (ans < inf)
printf("%d\n", ans);
else printf("No solution.\n");
}
return 0;
}