1. 程式人生 > >HDOJ3790 最短路徑問題 --- SPFA演算法求多關鍵字最短路

HDOJ3790 最短路徑問題 --- SPFA演算法求多關鍵字最短路

Problem Description

給你n個點,m條無向邊,每條邊都有長度d和花費p,給你起點s終點t,要求輸出起點到終點的最短距離及其花費,如果最短距離有多條路線,則輸出花費最少的。

Input

輸入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)

Output

輸出 一行有兩個數, 最短距離及其花費。

Sample Input

3 2 1 2 5 6 2 3 4 5 1 3 0 0

Sample Output

9 11

題解:

SPFA演算法是西南交通大學計算機學院段凡丁院長提出,相當於是Bellman-ford演算法的優化。

在SPFA中,先將起點入佇列,一個while迴圈取出隊首元素,找出與其鄰接的節點,判斷是否能夠鬆弛,如果可以鬆弛,就更新最短路,並將其入隊。最終可以計算出單源最短路徑(如果一個節點入隊超過N次,則說明不存在最短路,即有負權迴路,N為節點個數)。

SPFA通常採用鄰接表,此題有2個關鍵字:路徑長度和花費。  只需要在判斷鬆弛條件的時候改變下就行,如果鬆弛後的長度和不鬆弛相等,就判斷兩種情況下的花費,如果鬆弛後的花費可以更小,則鬆弛節點。

另外需要額外一個數組記錄下所有節點當前最小花費。

#include <cstdio>
#include <algorithm>
#include <vector>
#include <cstring>
#include <queue>
#define INF 0x3fffffff
using namespace std;
int n,m;
int s,t;
struct Node {
    int to,weight,cost;
    void set(int a,int b,int c) {
        to = a,weight = b,cost = c;
    }
}node[200010];
struct Point {
    int dis,cost;
}point[1010];
int cur;
vector<Node> vec[1010];
queue<int> q;

// SPFA最短路徑
void SPFA() {
    q.push(s);
    int temp;
    int cnt;
    while(!q.empty()) {
        temp = q.front();
        q.pop();
        // 遍歷周圍的點
        cnt = vec[temp].size();
        for(int i = 0;i < cnt;i++) {
            int to = vec[temp][i].to;
            int weight = vec[temp][i].weight;
            int cost = vec[temp][i].cost;
            // 判斷是否需要鬆弛
            if(point[to].dis > point[temp].dis + weight) {
                point[to].dis = point[temp].dis + weight;
                point[to].cost = point[temp].cost + cost;
                q.push(to);
            } else if((point[to].dis == point[temp].dis + weight) && (point[to].cost > point[temp].cost + cost) ) {
                point[to].cost = point[temp].cost + cost;
                q.push(to);
            }
        }
    }
    printf("%d %d\n",point[t].dis,point[t].cost);
}

int main() {
    while(scanf("%d%d",&n,&m) != EOF) {
        if(n == 0 && m == 0) break;
        memset(vec,0,sizeof(vec));
        while(!q.empty())
            q.pop();
        cur = 0;
        //vec.clear();
        for(int i = 1;i <= n;i++) {
            point[i].dis = INF;
            point[i].cost = INF;
        }
        int a,b,c,d;
        for(int i = 0;i < m;i++) {
            scanf("%d%d%d%d",&a,&b,&c,&d);
            node[cur].set(b,c,d);
            vec[a].push_back(node[cur]);
            cur++;
            node[cur].set(a,c,d);
            vec[b].push_back(node[cur]);
            cur++;
        }
        scanf("%d%d",&s,&t);
        point[s].dis = 0;
        point[s].cost = 0;
        SPFA();
    }
    
    return 0;
}