题目链接
题目描述:
在每年的校赛里,所有进入决赛的同学都会获得一件很漂亮的t-shirt。但是每当我们的工作人员把上百件的衣服从商店运回到赛场的时候,却是非常累的!所以现在他们想要寻找最短的从商店到赛场的路线,你可以帮助他们吗?
输入描述:
输入包括多组数据。每组数据第一行是两个整数N、M(N<=100,M<=10000),N表示成都的大街上有几个路口,标号为1的路口是商店所在地,标号为N的路口是赛场所在地,M则表示在成都有几条路。N=M=0表示输入结束。接下来M行,每行包括3个整数A,B,C(1<=A,B<=N,1<=C<=1000),表示在路口A与路口B之间有一条路,我们的工作人员需要C分钟的时间走过这条路。
输入保证至少存在1条商店到赛场的路线。
输出描述:
对于每组输入,输出一行,表示工作人员从商店走到赛场的最短时间
样例输入
2 1
1 2 3
3 3
1 2 5
2 3 5
3 1 2
0 0
样例输出:
3
2
由题可知,点少线多,所以可以优先考虑 稠密图 ,用 传递闭包 即可通过。先上代码,后序还有 传递闭包 的证明哦。
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int N = 101;
const int inf = 0x3f3f3f3f;
int dp[N][N];
int main() {
int n, m, x, y, k;
while(scanf("%d%d", &n, &m), n != 0) {
memset(dp, inf, sizeof(dp));
for(int i = 1; i <= m; ++i) {
scanf("%d%d%d", &x, &y, &w);
dp[x][y] = dp[y][x] = w;
}
for(int i = 1; i <= n; ++i) dp[i][i] = 0;
// 传递闭包的三重循环
for(int k = 1; k <= n; ++k)
for(int i = 1; i <= n; ++i)
for(int j = 1; j <= n; ++j)
dp[i][j]=min(dp[i][j],dp[i][k]+dp[k][j]);
printf("%d\n", dp[1][n]);
}
return 0;
}
另外我想借此来证明一下 传递闭包 的正确性。证:
传递闭包是三重循环结构,其分别是 枚举过渡点、枚举起点、枚举终点(起点和终点可以看做一体),但是你有没有考虑过 先枚举起点、终点, 再枚举过渡点(方案二) 是否正确呢, 接下来我们就具体的证明一下这两种方案的正确性:
我们首先需要建立一个模型(起点和终点固定,枚举过渡点),如图:
通过 枚举过渡点 来更新路径(减少起点到终点路径中的过渡点(当某条路径中的过渡点个数为零时 更新起点到终点的距离))
(说明:方案一,先确定过渡点,枚举起点和终点,再枚举过渡点
方案二,先确定起点和终点,枚举过渡点,再枚举起点和终点)
单轮这个模型,我们就能判断出方案二是错误的,因为在枚举上侧路径中的过渡点时,无法更新路径,而这个模型适合方案一。
最后我们把这个基础的模型通过变换和嵌套就可以得到一个图,反之一个图可以拆解为多个这样的模型,所以可证方案一是适合一个图的。