题目
给一个 n(1≤n≤2500) 个点 m(1≤m≤6200) 条边的无向图,求 s 到 t 的最短路。
输入格式
第一行四个由空格隔开的整数 n、m、s、t。
之后的 m 行,每行三个正整数 si、ti、wi(1≤wi≤109),表示一条从 si 到 ti 长度为 wi 的边。
输出格式
一个整数表示从 s 到 t 的最短路长度。数据保证至少存在一条道路。
样例
Inputcopy | Outputcopy |
---|---|
7 11 5 4 2 4 2 1 4 3 7 2 2 3 4 3 5 7 5 7 3 3 6 1 1 6 3 4 2 4 3 5 6 3 7 2 1 | 7 |
思路
Floyd算法的复杂度为O(n^3),Dijkstra算法的复杂度为O(n^2),观察本题的数据范围,用Floyd会超时,所以选择使用Dijkstra。
通过Dijkstra计算图中的最短路径时,需要指定起点s(即从顶点s开始计算)。
此外,引进两个集合S和U。S的作用是记录已求出最短路径的顶点(以及相应的最短路径长度),而U则是记录还未求出最短路径的顶点(以及该顶点到起点s的距离)。
初始时,S中只有起点s;U中是除s之外的顶点,并且U中顶点的路径是"起点s到该顶点的路径"。然后,从U中找出路径最短的顶点,并将其加入到S中;接着,更新U中的顶点和顶点对应的路径。 然后,再从U中找出路径最短的顶点,并将其加入到S中;接着,更新U中的顶点和顶点对应的路径……重复该操作,直到遍历完所有顶点。
代码
#include<iostream>
using namespace std;
long long n, m, s, t, r[6205][6205], d[6205];
bool vis[6205];
int main()
{
ios::sync_with_stdio(0);
cin.tie(0);
cin >> n >> m >> s >> t;
for (int i = 1; i <= n; ++i)
{
for (int j = 1; j <= n; ++j)
{
r[i][j] = 1e9 + 1;
}
}
for (int i = 1; i <= n; ++i)
{
d[i] = 1e9 + 1;
}
d[s] = 0;
for (int i = 1; i <= m; ++i)
{
long long u, v, x;
cin >> u >> v >> x;
if (x < r[u][v])
{
r[u][v] = r[v][u] = x;
}
}
while (1)
{
long long minn = 1e9 + 1, maxn = -1;
for (int i = 1; i <= n; ++i)
{
if (!vis[i] && minn > d[i])
{
minn = d[i];
maxn = i;
}
}
if (maxn == -1)
{
break;
}
vis[maxn] = 1;
for (int i = 1; i <= n; ++i)
{
if (!vis[i])
{
d[i] = min(d[i], d[maxn] + r[maxn][i]);
}
}
}
cout << d[t];
return 0;
}