-
问题建模
该问题本质上是一个在一个具有
个节点和
条边的有向带权图(Directed Weighted Graph)中,计算单源最短路径(Single Source Shortest Path, SSSP)与其逆问题的组合。
题目要求分别计算
的最短路径和
的最短路径,并求其总和。
即求解目标为:
其中
表示节点
到
的最短路径耗时。
-
约束分析
- 规模约束:
,
。虽然
较小,但
较大。
- 权重约束:
(非负权边),这为选择 Dijkstra 算法提供了理论基础,避免了 Bellman-Ford 或 SPFA 在负权情况下的性能开销。
- 复杂度陷阱:
- 如果是无向图,
,只需计算一次。但本题为单向图,往返路径不同。
- 直接对每个节点
(
) 分别运行一次最短路算法来计算
会导致调用
次算法,产生巨大的冗余计算。
- 如果是无向图,
- 规模约束:
核心算法
为了满足性能要求并保持代码的清晰,采用 两次 Dijkstra + 反向图(Transpose Graph) 的策略。
1. 算法范式选择:Dijkstra (堆优化版)
- 选择理由:由于边权均为正数,Dijkstra 是求解单源最短路径的最优选择。
- 数据结构:使用 二叉堆(优先队列) 优化,能够在处理稠密图或稀疏图时保持稳定的对数级性能。
2. “多源归一”问题的转化:反向图技术
求解 是标准的单源最短路径问题,运行一次 Dijkstra 即可。
难点在于求解
(多源汇聚到一点)。
- 常规思路缺陷:若对每个起点
运行 Dijkstra,总时间复杂度为
。在本题数据规模下约为
次运算,这会超时(通常限制
指令/秒)。
- 优化策略:利用图的转置性质。
在原图
中,
的最短路径,等价于在反向图
(将所有边方向取反,权重不变)中
的最短路径。 即:
。
结论:通过构建反向图,我们将“多源到单点”的问题转化为“单点到多源”的问题。只需在 和
上各运行一次 Dijkstra,共两次即可解决问题。
代码实现
#include <bits/stdc++.h>
#include <limits>
using namespace std;
using ll = long long;
using pii = pair<int, int>;
static constexpr int INF = 1e9 + 7;
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
int n, m;
cin >> n >> m;
vector<vector<pii>> adj(n + 1);
vector<vector<pii>> rev_adj(n + 1);
for (int i = 0; i < m; i++) {
int u, v, w;
cin >> u >> v >> w;
adj[u].push_back({v, w});
rev_adj[v].push_back({u, w});
}
auto dijkstra = [](int start, int n, const vector<vector<pii>>& adj,
vector<int>& dist) {
dist.assign(n + 1, INF);
vector<bool> visited(n + 1, false);
priority_queue<pii, vector<pii>, greater<pii>> pq;
dist[start] = 0;
pq.push({0, start});
while (!pq.empty()) {
auto [d, u] = pq.top();
pq.pop();
if (visited[u])
continue;
visited[u] = true;
for (const auto&[v, w] : adj[u]) {
if (dist[u] + w < dist[v]) {
dist[v] = dist[u] + w;
pq.push({dist[v], v});
}
}
}
};
vector<int> dist;
dijkstra(1, n, adj, dist);
vector<int> rev_dist;
dijkstra(1, n, rev_adj, rev_dist);
ll total = 0LL;
for (int i = 2; i <= n; i++) {
total += (ll)dist[i] + (ll)rev_dist[i];
}
cout << total << endl;
}
复杂度分析
1. 时间复杂度
- Dijkstra (堆优化):单次运行的复杂度为
。
- 总复杂度:我们需要运行两次 Dijkstra,加上线性扫描求和。
- 代入规模:
。
。计算量约为
次操作,这远低于
的常规时限,属于极优解。
- 对比 Floyd-Warshall:Floyd 算法可以求出任意两点间距离,但复杂度为
,在本题中极大概率超时。
2. 空间复杂度
- 邻接表存储:需要存储原图和反向图,每条边存储两次。
- 辅助空间:距离数组
dist和优先队列均占用或
。
- 总空间:
,内存占用极低,完全符合要求。
3. 其他算法讨论
- SPFA 算法:若使用 SPFA (Queue-optimized Bellman-Ford),平均复杂度为
(k常数较小)。但在特意构造的网格图或稠密图中,SPFA 会退化为
。鉴于本题保证非负权,Dijkstra 是最稳定且无风险的选择。
- 稠密图优化:如果
接近
(即
),使用朴素版 Dijkstra (无堆优化,每次线性扫描找最小值) 复杂度为
,可能比
略优。但本题
远小于
,稀疏图特性明显,堆优化版本更佳。
总结
该问题的最优解法是:基于邻接表的双向图构建 + 两次堆优化 Dijkstra。通过反向图技巧处理回程路径,将原本潜在的 复杂度降维至
。

京公网安备 11010502036488号