priority_queue && Dijkstra 对优先队列优化的Dijkstra算法的理解 hdu 3790

先看看wiki的算法描述:

Let the node at which we are starting be called the initial node. Let the distance of node Y be the distance from the initial node to Y. Dijkstra's algorithm will assign some initial distance values and will try to improve them step by step.

  1. Assign to every node a tentative distance value: set it to zero for our initial node and to infinity for all other nodes.
  2. Mark all nodes unvisited. Set the initial node as current. Create a set of the unvisited nodes called the unvisited set consisting of all the nodes.
  3. For the current node, consider all of its unvisited neighbors and calculate their tentative distances. For example, if the current node A is marked with a distance of 6, and the edge connecting it with a neighbor B has length 2, then the distance to B (through A) will be 6 + 2 = 8. If this distance is less than the previously recorded tentative distance of B, then overwrite that distance. Even though a neighbor has been examined, it is not marked as "visited" at this time, and it remains in the unvisited set.
  4. When we are done considering all of the neighbors of the current node, mark the current node as visited and remove it from the unvisited set. A visited node will never be checked again.
  5. If the destination node has been marked visited (when planning a route between two specific nodes) or if the smallest tentative distance among the nodes in the unvisited set is infinity (when planning a complete traversal; occurs when there is no connection between the initial node and remaining unvisited nodes), then stop. The algorithm has finished.
  6. Select the unvisited node that is marked with the smallest tentative distance, and set it as the new "current node" then go back to step 3.

注意红色部分,当对当前节点u的相邻节点v执行完所有可能的松弛操作(relaxation)后,才能把u加入已访问节点集合,即从unvisited set移除。

根据上句话,优先队列中先弹出有离源点s最短距离的节点v,若是第一次出队,则用它执行可能的松弛操作,现假设松弛完后v节点当前的距离值d[v]还可被其它节点松弛,从而可更小。

若存在节点k,使它可对v进行松弛,则必有d[k]<d[v],因为是正权边,根据优先队列出队顺序,应有k比v先出队,从而该松弛操作已经进行过,所以当v出队后,不存在这样的k。

上述简易证明用到了边权非负的条件,否则,在v执行完所有可能的松弛操作后,v不能从unvisited set中移除,即此时v需要重复入队----这样就用到了Bellman Ford算法。

拿hdu 3790来练练:

最短路径问题

Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)
Total Submission(s): 9080    Accepted Submission(s): 2760


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
 
Source
 
Recommend
notonlysuccess
 
裸的Dijkstra,费用可看成是要考虑距离之外的次要因素:
#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<cmath>
#include<vector>
#include<queue>
#include<cstdlib>
#include<algorithm>

using namespace std;

#define LL long long
#define ULL unsigned long long
#define UINT unsigned int
#define MAX_INT 0x7fffffff
#define MAX_LL 0x7fffffffffffffff
#define MAX(X,Y) ((X) > (Y) ? (X) : (Y))
#define MIN(X,Y) ((X) < (Y) ? (X) : (Y))
#define INF 1000000000
#define MAXN 1111
#define MAXM 211111

struct node{
    int u, d, p;
    bool operator < (const node &rhs)const{     //定义优先顺序
        return d>rhs.d || (d==rhs.d&&p>rhs.p);
    }
};

struct edge{
    int u, v, d, p, nxt;
}e[MAXM];
int h[MAXN];

int n, m, cc, d[MAXN], c[MAXN];
bool done[MAXN];

void dijkstra(int s, int t){
    memset(done, 0, sizeof(done));
    for(int i=1; i<=n; i++) c[i]=d[i]=INF;   c[s]=d[s]=0;
    priority_queue<node> q;     q.push((node){s, 0, 0});
    while(!q.empty()){
        node x=q.top();  q.pop();
        int u=x.u;
        if(done[u]) continue;       done[u]=true;           //如果含有负权边,这么干会出错
        for(int i=h[u]; i!=-1; i=e[i].nxt){
            int v=e[i].v, ed=e[i].d, ep=e[i].p;
            if(d[v]>d[u]+ed || d[v]==d[u]+ed&&c[u]+ep<c[v]){
                d[v]=d[u]+ed;   c[v]=c[u]+ep;
                q.push((node){v, d[v], c[v]});
            }
        }
    }
}

void add(int u, int v, int d, int p){
    e[cc]=(edge){u, v, d, p, h[u]};
    h[u]=cc++;
    e[cc]=(edge){v, u, d, p, h[v]};
    h[v]=cc++;
}

int main(){
//    freopen("C:\Users\Administrator\Desktop\in.txt","r",stdin);
    while(scanf(" %d %d", &n, &m)==2 && n){
        int i, u, v, td, p;           
        memset(h, -1, sizeof(h));   cc=0;
        for(i=0; i<m; i++){
            scanf(" %d %d %d %d", &u, &v, &td, &p);
            add(u, v, td, p);
        }
        scanf(" %d %d", &u, &v);
        dijkstra(u, v);
        printf("%d %d
", d[v], c[v]);
    }
    return 0;
}
原文地址:https://www.cnblogs.com/ramanujan/p/3341037.html