最短路徑問題 - 九度 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)(不包括保存圖所需的空間)。它同時適用於鄰接矩陣和鄰接鏈表形式保存的有向圖和無向圖。它求解從某一個特定的起點出發,到其它所有點的最短路徑,即單源最短路徑問題。