适用于非负权图,所有的边权都是非负数。
或者从s节点出发的子图中,边权都是非负数的也可以。(从s出发没办法适用的,是负数甚至复数都无所谓。)
Dijkstra算法
标准版本,带有vis数组方便检查是否从s出发可以到达i。
namespace Dijkstra {
const int MAXN = 2e5 + 10;
int n;
vector<pii> G[MAXN];
ll dis[MAXN];
bool vis[MAXN];
priority_queue<pli> PQ;
void dijkstra(int s) {
fill(dis + 1, dis + 1 + n, LINF);
fill(vis + 1, vis + 1 + n, 0);
while(!PQ.empty())
PQ.pop();
dis[s] = 0;
PQ.push({-dis[s], s});
while(!PQ.empty()) {
int u = PQ.top().second;
PQ.pop();
if(vis[u])
continue;
vis[u] = 1;
for(pii &p : G[u]) {
int v = p.first;
int w = p.second;
if(vis[v] || dis[v] <= dis[u] + w)
continue;
dis[v] = dis[u] + w;
PQ.push({-dis[v], v});
}
}
}
}
可能平均情况快一点的版本,拥有一个终点t,可以在非最坏情况下提前使得算法终止。由于算法提前终止所以除t以外的点不一定代表最终结果。
ll dijkstra(int s, int t) {
fill(dis + 1, dis + 1 + n, LINF);
fill(vis + 1, vis + 1 + n, 0);
while(!PQ.empty())
PQ.pop();
dis[s] = 0;
PQ.push({-dis[s], s});
while(!PQ.empty()) {
int u = PQ.top().second;
PQ.pop();
if(vis[u])
continue;
vis[u] = 1;
if(u == t)
break;
for(pii &p : G[u]) {
int v = p.first;
int w = p.second;
if(vis[v] || dis[v] <= dis[u] + w)
continue;
dis[v] = dis[u] + w;
PQ.push({-dis[v], v});
}
}
return dis[t];
}
计算从s出发到点i的最短路的条数
void dijkstra(int s) {
fill(dis + 1, dis + 1 + n, LINF);
fill(cnt + 1, cnt + 1 + n, 0);
fill(vis + 1, vis + 1 + n, 0);
while(!PQ.empty())
PQ.pop();
dis[s] = 0;
cnt[s] = 1;
PQ.push({-dis[s], s});
while(!PQ.empty()) {
int u = PQ.top().second;
PQ.pop();
if(vis[u])
continue;
vis[u] = 1;
for(pii &p : G[u]) {
int v = p.first;
int w = p.second;
if(vis[v] || dis[v] < dis[u] + w)
continue;
if(dis[v] == dis[u] + w) {
cnt[v] += cnt[u];
continue;
}
dis[v] = dis[u] + w;
cnt[v] = cnt[u];
PQ.push({-dis[v], v});
}
}
}
把默认的优先队列修改为“待修改操作的堆”或者“线段树”可以把空间复杂度从 (O(n+m)) 降低到 (O(n)) ,但是看起来很花里胡哨。