最短路径问题 - 九度 OJ 1008
题目
时间限制:1 秒 内存限制:128 兆 特殊判题:否
题目描述:
给你 n 个点,m 条无向边,每条边都有长度 d 和花费 p,给你起点 s 终点 t,要求输出起点到终点的最短距离及其花费,如果最短距离有多条路线,则输出花费最少的。
输入:
输入 n,m,点的编号是 1~n,然后是 m 行,每行 4 个数 a,b,d,p,表示 a 和 b之间有一条边,且其长度为 d,花费为 p。最后一行是两个数 s,t;起点 s,终点 t。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
来源:
2010 年浙江大学计算机及软件工程研究生机试真题
在该题中不仅需要求得起点到终点的最短距离,还需要在有多条最短路径的时,选取花费最少的那一条。要解决这个问题,只要更改 Dijstra 算法中关于“更近”的评判标准即可:有两条路径,若它们距离不一样时,距离小的更近;若距离一样时花费少的更近。当定义这种新的评判标准后,Dijstra 算法照样能求得“最近”的路径长度。
#include <stdio.h>
#include <vector>
using namespace std;
struct E{//邻接链表元素结构体
int next;
int c;
int cost;
};
vector<E> edge[1001];//邻接链表
int Dis[1001];//距离数组
int cost[1001];//花费数组
bool mark[1001];//是否属于集合K数组
int main()
{
int n,m;
int S,T;//起点,终点
while(scanf("%d%d",&n,&m)!=EOF){
if(n==0 && m==0)break;
for(int i=1;i<=n;i++)edge[i].clear();//初始化邻接链表
while(m--){
int a,b,c,cost;
scanf("%d%d%d%d",&a,&b,&c,&cost);
E tmp;
tmp.c=c;
tmp.cost=cost;//邻接链表中增加了该边的花费信息
tmp.next=b;
edge[a].push_back(tmp);
tmp.next=a;
edge[b].push_back(tmp);
}
scanf("%d%d",&S,&T);//输入起点终点信息
for(int i=1;i<=n;i++){//初始化
Dis[i]=-1;
mark[i]=false;
}
Dis[S]=0;
mark[S]=true;
int newP=S;//起点为S,将其加入集合K,且其最短距离确定为0
for(int i=1;i<n;i++){
for(int j=0;j<edge[newP].size();j++){
int t=edge[newP][j].next;
int c=edge[newP][j].c;
int co=edge[newP][j].cost;//花费
if(mark[t]==true)continue;
if(Dis[t]=-1 || Dis[t]>Dis[newP]+c ||
(Dis[t]=Dis[newP]+c && cost[t]>cost[newP]+co)){
//比较大小时,将距离相同但花费更短也作为更新的条件之一
Dis[t]=Dis[newP]+c;
cost[t]=cost[newP]+co;//更新花费
}
}
int min=123123123;
for(int j=1;j<=n;j++){
//选择最小值,选择时不用考虑花费的因素,
//因为距离最近的点的花费已经不可能由于经过其他点发生改变了
if(mark[j]==true)continue;
if(Dis[j]==-1)continue;
if(Dis[j]<min){
min=Dis[j];
newP=j;
}
}
mark[newP]=true;
}
printf("%d %d\n",Dis[T],cost[T]);//输出答案
}
return 0;
}
值得一提的是,若由结点 U 到结点 V 的最短路径不存在,即它们不连通,那么当 Dijstra 算法完成以后,V 结点仍然不属于集合 K。即当完成 Dijstra 算法后,mark[V]依然为 false 即说明,结点 U 到结点 V 的最短路不存在。
注:该最短路不存在,指结点 U 和 V 不连通的情况,我们不考虑存在负环的情况,边的权值为负这种特殊的情况在机试中考察的可能性不大,但若真的出现边的权值为负,若不存在负环则最短路存在,但我们不能使用 Dijstra 对其进行求解,因为 Dijstra算法原理在存在负权值的图上不成立;若存在负环则最短路不存在。要求解包含负权值边上的最短路问题,我们需要使用 SPFA 算法。
总结 Dijstra 算法的特点:它的时间复杂度为 O(N^2)(若在查找最小值处利用堆进行优化,则时间复杂度可以降到 O(N*logN),N 为结点的个数。空间复杂度为O(N)(不包括保存图所需的空间)。它同时适用于邻接矩阵和邻接链表形式保存的有向图和无向图。它求解从某一个特定的起点出发,到其它所有点的最短路径,即单源最短路径问题。