最短路体系
题目
一、单源最短路
Dijkstra
Dijkstra :一种求解单源最短路径的算法.无法处理 有负权图 。
核心原理 :贪心。每次锁定当前代价最小的点,它一定是最优的。
复杂度 :朴素 O ( n 2 + m ) O( n^2 + m ) O ( n 2 + m ) ,堆优化 O ( m log n ) O( m \log n ) O ( m log n ) 。
当 m ≈ n m \approx n m ≈ n 时 堆优化更高效;当 m ≈ n 2 m \approx n^{2} m ≈ n 2 时 堆优化反而低效。
Dijkstra 朴素 / 堆优化 模板
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 ll g[maxn][maxn];void dijkstra ( int s ) { fill ( dist, dist + n + 1 , inf ); dist[s] = 0 ; for ( int i = 1 ; i <= n; ++ i ) { int u = -1 ; for ( int v = 1 ; v <= n; ++ v ) { if ( !vis[v] && ( u == -1 || dist[v] < dist[u] ) ) u = v; } vis[u] = true ; for ( int v = 1 ; v <= n; ++ v ) { dist[v] = min ( dist[v], dist[u] + g[u][v] ); } } }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 struct Edge { int v, w; }; vector< Edge > adj[maxn];struct State { int u; ll d; bool operator < ( const State& oth ) const { return d > oth.d; } };void dijkstra ( int s ) { fill ( dist, dist + n + 1 , inf ); dist[s] = 0 ; priority_queue< State > pq; pq.push ({ s, 0 }); while ( !pq.empty ( ) ) { auto [u, d] = pq.top ( ); pq.pop ( ); if ( d > dist[u] ) continue ; for ( auto & [v, w] : adj[u] ) { if ( dist[u] + w < dist[v] ) { dist[v] = dist[u] + w; pq.push ({ v, dist[v] }); } } } }
Bellman-Ford
Bellman-Ford :一种求解单源最短路的算法,可以处理 有负权图 ,并可以对最短路不存在的情况进行判断。
SPFA :Bellman-Ford 的队列优化版本。
核心原理 :松弛。只要一个点的代价变小了,它的邻居就有可能变小。
复杂度 :Bellman-Ford O ( n ⋅ m ) O( n \cdot m ) O ( n ⋅ m ) ,SPFA O ( k ⋅ m ) O( k \cdot m ) O ( k ⋅ m ) 。
SPFA 复杂度下限等于 Bellman-Ford,因此在 非负权图 上很容易被卡死。
但在处理 有负权图 时,随机数据下优于 Bellman-Ford。
Bellman-Ford / SPFA - BFS 模板
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 struct Edge { int u, v, w; }; vector< Edge > edges; ll dist[maxn];int n, m, s;bool bellman_ford ( int s ) { fill ( dist, dist + n + 1 , inf ); dist[s] = 0 ; bool relaxed; for ( int i = 1 ; i <= n; ++ i ) { relaxed = false ; for ( auto &[u, v, w] : edges ) { if ( dist[u] != inf && dist[u] + w < dist[v] ) { dist[v] = dist[u] + w; relaxed = true ; if ( i == n ) return true ; } } if ( !relaxed ) break ; } return false ; }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 struct Edge { int v, w; }; vector< Edge > adj[maxn]; ll dist[maxn];int cnt[maxn];bool inq[maxn];int n, m;bool spfa ( int s ) { fill ( dist, dist + n + 1 , inf ); fill ( cnt, cnt + n + 1 , 0 ); fill ( inq, inq + n + 1 , false ); queue<int > q; q.push ( s ), inq[s] = true ; dist[s] = 0 ; while ( !q.empty ( ) ) { int u = q.front ( ); q.pop ( ), inq[u] = false ; for ( auto & [v, w] : adj[u] ) { if ( dist[u] + w < dist[v] ) { dist[v] = dist[u] + w; cnt[v] = cnt[u] + 1 ; if ( cnt[v] >= n ) return true ; if ( !inq[v] ) q.push ( v ), inq[v] = true ; } } } return false ; }
二、负环与差分约束
SPFA 求负环
SPFA - DFS 模板
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 struct Edge { int v, w; }; vector< Edge > adj[maxn]; ll dist[maxn];bool vis[maxn];int n, m;bool spfa ( int u ) { vis[u] = true ; for ( auto & [v, w] : adj[u] ) { if ( dist[u] + w < dist[v] ) { dist[v] = dist[u] + w; if ( vis[v] ) return true ; if ( spfa ( v ) ) return true ; } } vis[u] = false ; return false ; }
差分约束
差分约束 :求解形如 X u − X v ≤ w X_u - X_v \leq w X u − X v ≤ w 不等式组,利用最短路算法寻找一组可行解的方法。
核心原理 :映射(松弛)。利用三角不等式 d i s t [ v ] ≤ d i s t [ u ] + w dist[v] \le dist[u] + w d i s t [ v ] ≤ d i s t [ u ] + w 对偶 X v ≤ X u + w X_v \le X_u + w X v ≤ X u + w 。若存在负环,说明不等式组存在逻辑冲突(系统无解)。
X v ≤ X u + w X_{v} \leq X_{u} + w X v ≤ X u + w ,连一条 u → v u \to v u → v 的权值为 w w w 的边。
若出现 X u − X v ≥ w X_{u} - X_{v} \geq w X u − X v ≥ w 的情况,则两侧乘 − 1 -1 − 1 得到 X v − X u ≤ − w X_{v} - X_{u} \leq -w X v − X u ≤ − w 。
若出现 X u − X v = w X_{u} - X_{v} = w X u − X v = w 的情况,则拆分成 X u − X v ≤ w X_{u} - X_{v} \leq w X u − X v ≤ w 和 X v − X u ≥ w X_{v} - X_{u} \geq w X v − X u ≥ w 即可。
三、多源最短路与传递闭包
Floyd 多源最短路
Floyd 模板
注意:最外层循环必须是中转点 k k k 。
1 2 3 4 5 6 7 8 9 ll d[505 ][505 ];void floyd ( int n ) { for ( int k = 1 ; k <= n; ++ k ) for ( int i = 1 ; i <= n; ++ i ) for ( int j = 1 ; j <= n; ++ j ) if ( d[i][k] != inf && d[k][j] != inf ) d[i][j] = min ( d[i][j], d[i][k] + d[k][j] ); }
Floyd 传递闭包
四、状态空间转换
分层图
分层图 :当决策包含 “特殊能力”(如 K K K 次免费、消除某条边)时,使用的一种 扩展状态 的建图方法。
核心原理 :状态拓展。将状态从一维的点 u u u 升维到二维 ( u , k ) (u, k) ( u , k ) 。同层正常连边代价为 w w w ,跨层连边代表使用能力,代价通常为 0 0 0 。
相关例题 :P4568 飞行路线 | P1073 最优贸易
同余最短路
同余最短路 :利用模运算将无限 的值域空间压缩到有限 的同余环中。
解决极大容量的完全背包可行性问题。
核心原理 :周期性覆盖。选取 m = min ( a i ) m = \min(a_i) m = min ( a i ) 为模数,若模 m m m 余 r r r 的最小值 d i s t [ r ] dist[r] d i s t [ r ] 被凑出,则所有 d i s t [ r ] + k ⋅ m dist[r] + k \cdot m d i s t [ r ] + k ⋅ m 均可被凑出。因此将 d i s t [ r ] dist[r] d i s t [ r ] 作为状态,边权为 a k a_k a k ,在图上跑最短路。
相关例题 :P2371 墨墨的等式 | ABC443F Non-Increasing Number
五、其他派生
次短路
最短路图
最短路图 :除去原图中所有非最优的边,剩下的边构成一个 DAG,是处理 “最短路方案数” 或 “公共路径” 的骨架模型。
核心原理 :三角等式判定。若边 ( u , v ) (u, v) ( u , v ) 在起点到终点的最短路上,必定满足:d i s t s t a r t [ u ] + w ( u , v ) + d i s t e n d [ v ] = = d i s t s t a r t [ e n d ] dist_{start}[u] + w(u, v) + dist_{end}[v] == dist_{start}[end] d i s t s t a r t [ u ] + w ( u , v ) + d i s t e n d [ v ] == d i s t s t a r t [ e n d ] 。