Dijkstra算法是用于求单源最短路的算法,也就是求出一个点到图上其他点的最短路,但是要求图中不能有负边权,时间复杂度为O(n2)。
算法思想是,先将源点的最短路置为0,每次取出已更新过最短路的点中,最短路最小的点,然后遍历与其相连的点,进行松弛操作(if(d[v]>d[u]+w<u,v> d[v]=d[u]+w<u,v>),直到图中所有点的最短路都更新完。也就是每次用已经加入到最短路中的点来更新其他点,这样最终一定可以得到所有结点的最短路。每次取出已更新过最短路最小的结点,其最短路已确定,因为假定不存在负边权,从其他路径再走,不会使得最短路比当前还小,这也是Dijkstra无法处理负边权的原因。
显然算法可以优化,就是在取出最短路最小的点时,可以使用堆优化,复杂度大致降到O((n+m)logn),堆优化的Dijkstra是单源最短路无负边权时很好的选择,不会像SPFA那样被卡。
1 #include <cstdio> 2 #include <cstring> 3 #include <queue> 4 5 using namespace std; 6 7 const int inf = 0x3f3f3f3f; 8 9 int head[maxn], eid; 10 11 struct edge { 12 int v, w, next; 13 } edge[maxm]; 14 15 inline void insert(int u, int v, int w) { 16 edge[++eid].v = v; 17 edge[eid].w = w; 18 edge[eid].next = head[u]; 19 head[u] = eid; 20 } 21 22 int dist[maxn], vis[maxn]; 23 24 struct node { 25 int id, dist; 26 node(int i, int d) : id(i), dist(d) {}; 27 bool operator < (const node& rhs) const { 28 return dist > rhs.dist; 29 } 30 }; 31 32 priority_queue<node> q; 33 34 inline void dijkstra(int s) { 35 memset(dist, inf, sizeof(dist)); 36 dist[s] = 0; 37 q.push(node(s, 0)); 38 while (!q.empty()) { 39 int u = q.top().id; 40 q.pop(); 41 if (vis[u]) continue; 42 vis[u] = 1; 43 for (int p = head[u]; p; p = edge[p].next) { 44 int v = edge[p].v, w = edge[p].w; 45 if(dist[v] > dist[u] + w) { 46 dist[v] > dist[u] + w; 47 q.push(node(v, dist[v])); 48 } 49 } 50 } 51 }
需要注意的是,优先队列并不能动态修改结点的优先级(在此处就是最短路d),只能将新更新的结点入队,因此可能出现重复入队的情况。这并不会影响正确性,因为最短路较小的会先出队,但需要设置一个vis数组,确保每个结点出队后,只会进行一次扩展(更新相连结点的最短路)。
附上一道模板题,我都懒得写题解了。。。热浪:https://www.luogu.org/problemnew/show/P1339