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.
- Assign to every node a tentative distance value: set it to zero for our initial node and to infinity for all other nodes.
- 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.
- 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.
- 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.
- 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.
- 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)
(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\n", d[v], c[v]); } return 0; }