分层图
将原问题中的额外状态(如剩余步数、资源数量、模数等)作为 " 层 " 来扩展图,从而将复杂约束转化为普通的最短路问题。
| 算法 | 核心思想 | 适用 | 时间复杂度 | 能否处理负权边 | 能否处理负环 |
|---|---|---|---|---|---|
| Dijkstra | 贪心:从起点开始,每次从未钉死最短路的点中选取距离最近的点,并用它去"松弛"其邻接点 | 单源最短路,非负权图 | O(m log n) | 不能 | 不能 |
| Bellman-Ford | 暴力枚举边 :对所有边进行 n-1 次松弛操作,每次尝试更新所有边终点的距离 |
单源最短路,存在负权边的有向图 | O(nm) | 能 | 能 |
| SPFA | 队列优化 BF:是 Bellman-Ford 算法的队列优化版,只对"被成功松弛"的点的出边进行松弛,避免了无效的边遍历 | 单源最短路,存在负权边的有向图 | 平均 O (km),最坏 O (nm) | 能 | 能 |
| Floyd | 区间 DP :通过枚举中间点 k 来松弛任意两点 i、j 之间的距离 |
多源最短路,稠密图 | O(n³) | 能 | 有向图可以处理 |
| Johnson | 贪心:通过一次 SPFA 来判断负环,然后跑 n 遍 Dijkstra | 多源最短路,稀疏图 | O(n² log n + nm) | 能 | 能 |
最短路模版
//Dijkstra
const int maxn = 100010;
struct node {
int dis, pos;
bool operator<(const node &x) const { return x.dis < dis; }
};
int n, m, s, t;
int dis[maxn];
bool vis[maxn];
vector<pair<int, int>> g[maxn];
priority_queue<node> q;
void dijkstra() {
for (int i = 1; i <= n; i++) {
dis[i] = 0x3f3f3f3f;
vis[i] = 0;
}
dis[s] = 0;
q.push({ 0, s });
while (!q.empty()) {
node tmp = q.top();
q.pop();
int x = tmp.pos;
if (vis[x]) {
continue;
}
vis[x] = 1;
for (auto [y, w] : g[x]) {
if (dis[y] > dis[x] + w) {
dis[y] = dis[x] + w;
if (!vis[y]) {
q.push({ dis[y], y });
}
}
}
}
}
//SPFA
const int maxn = 1e4 + 5;
const int inf = 1e9;
int n, m, s, u, v, w, t;
int dis[maxn];
bool vis[maxn];
vector<pair<int, int>> g[maxn];
void spfa() {
for (int i = 1; i <= n; i++) {
dis[i] = inf;
}
queue<int> q;
q.push(s);
vis[s] = 1;
dis[s] = 0;
while (!q.empty()) {
int t = q.front();
q.pop();
vis[t] = 0;
for (int i = 0; i < g[t].size(); i++) {
int to = g[t][i].first;
int w = g[t][i].second;
if (dis[to] > dis[t] + w) {
dis[to] = dis[t] + w;
if (vis[to]) {
continue;
}
vis[to] = 1;
q.push(to);
}
}
}
}
//Floyd
for (int k = 1; k <= n; k++) {
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= n; j++) {
dis[i][j] = min(dis[i][j], dis[i][k] + dis[k][j]);
}
}
}
//Johnson
const int maxn = 3e3 + 5;
const int INF = 1e9;
struct node {
int dis;
int pos;
bool operator<(const node& oth) const { return dis > oth.dis; }
};
int n, m;
vector<pair<int, int> > g[maxn];
vector<pair<int, int> > g2[maxn];
int dis[maxn];
int cnt[maxn];
bool inq[maxn];
int h[maxn];
int ans[maxn][maxn];
bool SPFA(int s) {
queue<int> q;
for (int i = 0; i <= n; i++) {
dis[i] = INF;
cnt[i] = 0;
inq[i] = 0;
}
dis[s] = 0;
inq[s] = 1;
q.push(s);
while (!q.empty()) {
int u = q.front();
q.pop();
inq[u] = 0;
for (auto v : g[u]) {
int nod = v.first;
int w = v.second;
if (dis[u] + w < dis[nod]) {
dis[nod] = dis[u] + w;
if (!inq[nod]) {
q.push(nod);
inq[nod] = 1;
cnt[nod]++;
if (cnt[nod] > n) {
return 0;
}
}
}
}
}
for (int i = 1; i <= n; i++) {
h[i] = dis[i];
}
return 1;
}
void dijkstra(int s) {
priority_queue<node> q;
for (int i = 1; i <= n; i++) dis[i] = INF;
memset(inq, 0, sizeof(inq));
dis[s] = 0;
q.push({ 0, s });
while (!q.empty()) {
int u = q.top().pos;
q.pop();
if (inq[u])
continue;
inq[u] = 1;
for (auto v : g2[u]) {
int nod = v.first;
int w = v.second;
if (dis[nod] > dis[u] + w) {
dis[nod] = dis[u] + w;
if (!inq[nod]) {
q.push({ dis[nod], nod });
}
}
}
}
for (int i = 1; i <= n; i++) {
if (dis[i] >= INF) {
ans[s][i] = INF;
} else {
ans[s][i] = dis[i] - h[s] + h[i];
}
}
}