问题描述:
给你n个点,m条无向边,每条边都有长度d和花费p,给你起点s终点t,要求输出起点到终点的最短距离及其花费,如果最短距离有多条路线,则输出花费最少的。
输入:
输入n,m,点的编号是1~n,然后是m行,每行4个数 a,b,d,p,表示a和b之间有一条边,且其长度为d,花费为p。最后一行是两个数 s,t;起点s,终点。n和m为0时输入结束。(1<n<=1000, 0<m<100000, s != t)
输出:
输入n,m,点的编号是1~n,然后是m行,每行4个数 a,b,d,p,表示a和b之间有一条边,且其长度为d,花费为p。最后一行是两个数 s,t;起点s,终点。n和m为0时输入结束。(1<n<=1000, 0<m<100000, s != t)
样例输入:
3 2
1 2 5 6
2 3 4 5
1 3
0 0
样例输出:
9 11
最近想练练DijkStra,在LeetCode上愣是没找到相关的题,于是跑到HDU上选了这么一道题。
该题要求我们在保证最短路径的基础上花费越小越好,所以我们不仅要在距离更短时更新距离和花费,在距离相等时也要选择更少的花费。
该题没有要求我们输出最短路径,实在遗憾,这里使用了一个前驱数组pre记录了各点的前驱,根据pre逆序查找即可得到最短路径。
同一套算法,一开始我使用Java代码编写提交后被判定超时(超出2000ms),改用C++实现只需要312ms完美AC,看来还是C++比较适合在OJ上刷题。当然,Java在LeetCode上还是很好用的。
C++源码如下:
#include<iostream>
#include<limits>
using namespace std;
int n; // n个点,点的编号从1开始
int m; // m条边
const int MAXN = 1000 + 10;
int topo[MAXN][MAXN]; // 邻接矩阵,值为距离
int cost[MAXN][MAXN]; // 花费
int start;
int end;
void dijkstra(int start, int end)
{
int dist[MAXN]; // 起点到该点的最短路径长度
int pathCost[MAXN]; // 起点到该点的最短路径花费
bool S[MAXN]; // 已求得最短路径的顶点集
int pre[MAXN]; // 最短路径上该点的前驱,用来记录最短路径
for (int i = 1; i <= n; i++)
{
dist[i] = topo[start][i]; // 初始时为起点到该点的长度
if (dist[i] < numeric_limits<int>::max())
pre[i] = start;
pathCost[i] = cost[start][i];
S[i] = false; // 不要忘了给bool型初始化,不然有可能是ture
}
S[start] = true; // 初始时S中只有起点
for (int i = 1; i < n; i++)
{
int minDist = numeric_limits<int>::max();
int u = 0;
for (int j = 1; j <= n; j++)
{
if (!S[j] && dist[j] < minDist)
{
minDist = dist[j];
u = j; // 到该点已是最短路径,将作为中间结点进行松弛操作
}
}
S[u] = true; // 加入到S中
for (int j = 1; j <= n; j++)
{
if (!S[j] && topo[u][j] < numeric_limits<int>::max()) // 对所有从结点u发出的边(u, j)进行松弛操作
{
if (dist[u] + topo[u][j] < dist[j])
{
dist[j] = dist[u] + topo[u][j];
pathCost[j] = pathCost[u] + cost[u][j];
pre[j] = u; // 更新前驱结点
}
else if (dist[u] + topo[u][j] == dist[j] && pathCost[u] + cost[u][j] < pathCost[j])
{
pathCost[j] = pathCost[u] + cost[u][j];
}
}
}
}
printf("%d %d\n", dist[end], pathCost[end]);
}
int main()
{
int a, b, d, p;
int start, end;
while (scanf("%d%d", &n, &m), n || m)
{
for (int i = 1; i <= n; i++) // 初始化邻接矩阵
{
for (int j = 1; j <= n; j++)
{
if (i == j)
topo[i][j] = 0;
else
topo[i][j] = numeric_limits<int>::max();
}
}
for (int i = 0; i < m; i++) // 构造邻接矩阵
{
scanf("%d%d%d%d", &a, &b, &d, &p);
if (d < topo[a][b]) // 取距离最小的边
{
topo[a][b] = topo[b][a] = d;
cost[a][b] = cost[b][a] = p;
}
else if (d == topo[a][b] && p < cost[a][b]) // 距离相同,取花费最小的边
{
cost[a][b] = cost[b][a] = p;
}
}
scanf("%d%d", &start, &end);
dijkstra(start, end);
}
return 0;
}
显然,DijkStra算法的时间复杂度为O(n^2)