DIJKSTRA
注意Dijkstra不能处理带负权的图,若带负权请使用spfa外加判断各点入队的次数。
Dijkstra的过程跟之前我写过的Prim最小生成树的算法很类似,貌似没差别。当边不是很密集的时候bfs就可以搞定,当边比较多时Dijkstra比较稳定。所以对于边密集类型用常用邻接矩阵来存图,vis【】数组来判定到达某点的最短路径是否已经确定(有点像Prim中判断点是否进入树中),起点必然是确定的,接着桶排思想枚举剩下的点找到到已经确定的点的区域(类似Prim中的树)距离最短的点,标记进入确定最短的点集,由于新点的加入,再枚举一次剩下的点,更新到这个新生成的点域的最小距离。总上为一个大循环(每次大循环结束确定一个新的点,故循环N-1次),两个内嵌的小循环,跟Prim一毛一样~。所以其实最小生成树就是一个图单源最短路径的一个体现(个人见解,若有不当欢迎指正)。复杂度为O(n^2)还是比较暴力的,一般使用堆优化和链式向前星的版本。我们先来看看最朴素的版本
#define Inf 0x3f3f3f3f const int maxn = 1e3 + 2; int dis[maxn], table[maxn][maxn], N; bool vis[maxn]; //记录是否已经找到达该点的最小值 void dijkstra() { for(int i=1; i<=N; ++i) dis[i] = table[i][s]; dis[1] = 0; memset(vis, false, sizeof(vis)); vis[s] = true; for(int i=1; i<=N; ++i){ int index = -1, mindis = Inf; for(int j=1; j <= N; ++j){ if(!vis[j] && dis[j] < mindis){ // 遍历所有点找到到确定点集距离最小的点 mindis = dis[j]; index = j; } } if(index = -1) break; //最短路不存在 vis[index] = true; // index点到源点的最短路确定! for(int j=1; j<=N; ++j){ // 更新剩余点的最短路 if(dis[j] > dis[index] + table[index][j] && !vis[j]) dis[j] = dis[index] + table[index][j]; } } return; }
优化
下面对朴素的算法进行优化。1)邻接表存图:减少不必要的点的遍历。2)利用priotity_queue:堆优化找点的过程,这样子找点的复杂度就由原本的O(N)降为O(logN),之后的更新操作由于是邻接表只要遍历临边,故复杂度上限为O(ElogN)。下面这个来自poj的一道题,这里的dijkstra用了priority_queue优化,其实就是将大循环里的第一个循环找最小的节点的操作让priority_queue自行完成。题目为Sightseeing。这里的dijkstra不仅堆优化,而且同时查找了最短路和次短路,所以原本的数组都增加了一维。
#include <cstdio> #include <cstring> #include <iostream> #include <queue> using namespace std; #define ll long long #define Inf 2147483647 const int maxe = 50000 + 5 maxv = 50000 + 5; struct Edge{ int to, w; int next; Edge(int a=0, int b=0, int c=0):to(a), w(b), next(c){} }e[maxe * 2]; int head[maxv], cnte, pw[amxv]; struct Node{ int u; ll dis; Node(int a=0, ll b=0):u(a), dis(b){} bool operator < (const Node& b) const{ return this->dis > b.dis; } }; bool vis[maxv]; ll ans, dis[maxv]; int nv, ne, a, b, w, st; inline void add_edge(const int& from, const int& to, const int& w){ ++cnte; e[cnte].to = to, e[cnte].w = w; e[cnte].next = head[from]; head[from] = cnte; return; } void dijkstra(){ memset(vis, false, sizeof(vis)); for(int i=1; i<=nv; ++i) dis[i] = Inf; //注意这里的Inf不是0x3f3f3f3f只能用循环赋值 dis[st] = 0; priority_queue<Node> pq; while(pq.size()) pq.pop(); pq.push(Node(st, 0)); while(pq.size()){ Node cur = pq.top(); pq.pop(); if(vis[cur.u]) continue; vis[cur.u] = true; for(int i = head[cur.u]; i; i = e[i].next) { if(dis[e[i].to] > dis[cur.u] + e[i].w){ dis[e[i].to] = dis[cur.u] + e[i].w; pq.push(Node(e[i].to, dis[e[i].to])); //无需再判断是否已经确定,直接入队即可,因为每次弹出的值都是经过筛选的最小值 //且访问过的不会再 } else continue; } } return; }
#include <iostream> #include <cstdio> #include <algorithm> #include <queue> #include <cstring> using namespace std; const int N = 1e3 + 5, M = 1e4 + 5, Inf = 0x3f3f3f3f; struct Edge{ int to, w; int next; }e[M]; int head[N], cnte; int t, n, m, u, v, w, st, en; int dis[N][2], vis[N][2], cnt[N][2]; struct Node{ int index, dis, type; Node(int a=0, int b=0, int c=0):index(a), dis(b), type(c){} bool operator < (const Node& b) const{ return this->dis > b.dis; } };//看到这各重载应该就懂要干嘛了吧,对,用priority_queue优化dijkstra! void dijkstra(){ priority_queue<Node> pq; memset(vis, 0, sizeof(vis)); memset(cnt, 0, sizeof(cnt)); for(int i=1; i<=n; ++i) dis[i][0] = dis[i][1] = Inf; dis[st][0] = 0; cnt[st][0] = 1;//最短路确定存在 // pq.push({st, 0, 0}); pq.push(Node(st, 0, 0)); while(pq.size()){ Node cur = pq.top(); pq.pop(); if(vis[cur.index][cur.type]) continue; vis[cur.index][cur.type] = true; for(int i = head[cur.index]; i; i = e[i].next) { int nindex = e[i].to, w = e[i].w; if(dis[nindex][0] > dis[cur.index][cur.type] + w){ dis[nindex][1] = dis[nindex][0]; cnt[nindex][1] = cnt[nindex][0]; dis[nindex][0] = dis[cur.index][cur.type] + w; cnt[nindex][0] = cnt[cur.index][cur.type]; // pq.push({nindex, dis[nindex][0], 0}); // pq.push({nindex, dis[nindex][1], 1}); pq.push(Node(nindex, dis[nindex][0], 0)); pq.push(Node(nindex, dis[nindex][1], 1)); } else{ if(dis[nindex][0] == dis[cur.index][cur.type] + w) cnt[nindex][0] += cnt[cur.index][cur.type]; else{ if(dis[nindex][1] > dis[cur.index][cur.type] + w){ dis[nindex][1] = dis[cur.index][cur.type] + w; cnt[nindex][1] = cnt[cur.index][cur.type]; // pq.push({nindex, dis[nindex][1], 1}); pq.push(Node(nindex, dis[nindex][1], 1)); } else{ if(dis[nindex][1] == dis[cur.index][cur.type] + w) cnt[nindex][1] += cnt[cur.index][cur.type]; } } } } } return; } inline void add_edge(const int& from, const int& to, const int& w){ ++cnte; e[cnte].to = to; e[cnte].w = w; e[cnte].next = head[from]; head[from] = cnte; return; } inline int read(){ char ch = getchar(); int ans = 0; while(ch<'0' || ch>'9') ch = getchar(); while(ch >= '0' && ch <= '9'){ ans = (ans << 3) + (ans << 1) + ch - '0'; ch = getchar(); }return ans; } int main() { t = read(); while(t--){ memset(head, 0, sizeof(head)); cnte = 0; n = read(), m = read(); for(int i = 1; i <= m; ++i){ u = read(), v = read(), w = read(); add_edge(u, v, w); } st = read(); en = read(); dijkstra(); if(dis[en][1] == dis[en][0] + 1) cnt[en][0] += cnt[en][1]; printf("%d\n", cnt[en][0]); } return 0; }