最短路徑問題 - 九度 OJ 1008

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

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章