P2910 [USACO08OPEN] Clear And Present Danger S 题解
littlesnake · · 题解
前言
刚学习了 Floyd 算法,就想来写个题解,如果题解没有通过的话,就当作个人笔记好了。
我看题解区第一篇题解就只是简单讲了一下,又贴了个代码。我将在此题解中使用 2 种算法求解,比较它们的时间复杂度、空间复杂度,以及它们的耗时。并且讲解 Floyd 算法的原理。
分析
这道题是一个最短路题,但是它与其它题目不同之处在于它需要处理从不同点出发的最短路径,也就是多源最短路径。点的数量较少,但是任意两点之间都有连边(边的数量约为
代码如下:
#include <bits/stdc++.h>
#define MAXN 105
#define MAXM 10005
#define inf 2147483647
using namespace std;
// 有点混乱,d 数组是 Dijkstra 用的
int n, m, ans, a[MAXM], dis[MAXN][MAXN], d[MAXN];
bool vis[MAXN];
// 裸的 Dijkstra
int Dijkstra(int b, int e) {
memset(vis, 0, sizeof(vis)); // 需要初始化
for(int i = 1; i <= n; i++) d[i] = inf;
d[b] = 0;
while(!vis[e]) {
vis[b] = 1;
for(int i = 1; i <= n; i++) {
if(vis[i]) continue;
if(d[b] + dis[b][i] < d[i])
d[i] = d[b] + dis[b][i];
}
int minn = inf;
for(int i = 1; i <= n; i++) {
if(vis[i]) continue;
if(d[i] < minn) {
minn = d[i];
b = i;
}
}
}
return d[e];
}
int main() {
cin >> n >> m;
for(int i = 1; i <= m; i++) cin >> a[i];
for(int i = 1; i <= n; i++)
for(int j = 1; j <= n; j++)
cin >> dis[i][j];
for(int i = 1; i < m; i++)
ans += Dijkstra(a[i], a[i + 1]);
cout << ans;
return 0;
}
耗时 517ms。
但事实上,对于多源最短路,可以使用 Floyd-Warshall 算法(简称 Floyd 算法)更便捷地解决此类问题。
采用动态规划的思路,设
考虑如何进行状态更新,显然从
-
从
i 到j ,且只经过编号为1 到k - 1 的路径; -
从
i 到k ,再从k 到j ,且只经过编号为1 到k 的点的路径。
显然对于第一类路径,最短者的长度就是
然后只需要依次枚举
代码非常简短,如下所示:
for(int k = 1; k <= n; k++)
for(int i = 1; i <= n; i++)
for(int j = 1; j <= n; j++)
dis[i][j][k] = min(dis[i][j][k - 1], dis[i][k][k - 1] + dis[k][j][k - 1]);
时间复杂度
显然
空间复杂度变为
#include <bits/stdc++.h>
#define MAXN 105
#define MAXM 10005
using namespace std;
int n, m, a[MAXM], dis[MAXN][MAXN];
int main() {
cin >> n >> m;
for(int i = 1; i <= m; i++) cin >> a[i];
for(int i = 1; i <= n; i++)
for(int j = 1; j <= n; j++)
cin >> dis[i][j];
for(int k = 1; k <= n; k++)
for(int i = 1; i <= n; i++)
for(int j = 1; j <= n; j++)
dis[i][j] = min((long long)dis[i][j], (long long)dis[i][k] + dis[k][j]);
int ans = 0;
for(int i = 1; i <= m - 1; i++)
ans += dis[a[i]][a[i + 1]];
cout << ans;
return 0;
}
耗时 41ms。
可以证明,当存在负权边时 Floyd 算法正确性可以保证。Floyd 算法既可以处理无向图,也可以处理有向图,注意设置 dis 的初始值即可。
但是为什么 Floyd 算法和 Dijkstra 算法运行的时间相差这么多呢?
因为 Dijkstra 是单源最短路径算法,而 Floyd 是多源最短路径算法。
小结
每种算法都有自己擅长的方面,没有最好的算法,只有适合的题目。