
2026-01-18:边反转的最小路径总成本。用go语言,给定一个包含 n 个点(编号 0 到 n-1)的有向带权图。边集合 edges 中的每一项 edges[i] = [ui, vi, wi] 表示从 ui 指向 vi 的有向边,权重为 wi。 每个点都有一次特殊操作的机会:当你到达某个点 u 且还没使用过该点的这次机会时,你可以选择任意一条原本指向 u 的入边 v → u,把它临时改为 u → v 并立即沿着这条新方向移动一次。使用这次临时改向并穿越该边的代价为原边权的两倍(2*wi)。这种改向仅对这次移动生效,之后边的方向恢复且该点的机会就不能再用了。
求从起点 0 到终点 n-1 的最小可能总花费,若无法到达则返回 -1。
2 <= n <= 50000。
1 <= edges.length <= 100000。
edges[i] = [ui, vi, wi]。
0 <= ui, vi <= n - 1。
1 <= wi <= 1000。
输入: n = 4, edges = [[0,2,1],[2,1,1],[1,3,1],[2,3,3]]。
输出: 3。
解释:
不需要反转。走路径 0 → 2 (成本 1),然后 2 → 1 (成本 1),再然后 1 → 3 (成本 1)。
总成本为 1 + 1 + 1 = 3。
题目来自力扣3650。
算法首先将输入的有向图转换为一个无向图的邻接表,但其中蕴含了反转边的规则:
u -> v(权重为 w),在邻接表中创建两条边:u 到 v,权重为 w。这表示不进行反转,直接沿原方向移动。v 到 u,权重为 2 * w。这表示当位于节点 v 时,可以使用其特殊操作机会,将一条指向 v 的入边(即 u -> v)反转,并从 v 移动到 u,代价是原权重的两倍 。v 的特殊操作机会被隐式地建模为一条从 v 出发、指向其某个入边起点的、代价加倍的边 。dis,用于记录从起点 0 到每个节点的当前已知最短距离。初始时,起点 0 的距离设为 0,其他所有节点的距离设为极大值(math.MaxInt)。h,用于高效地获取当前未处理节点中距离起点最近的节点。堆中存储的是 (distance, node) 对。初始时,将起点 (0, 0) 加入堆中 。算法核心是一个循环,直到堆为空为止:
x 及其距离 disX 。disX 是否等于 dis[x] 数组中记录的值。如果 disX > dis[x],说明节点 x 已经被处理过(有更短的路径先到达了它),当前记录是过时的,直接跳过 。x 就是终点 n-1,则立即返回 dis[x] 作为答案,因为Dijkstra算法保证第一次处理终点时得到的就是最短路径 。x 的所有邻居(通过邻接表 g[x])。对于每条从 x 到 y、权重为 wt 的边:x 到达 y 的新距离 newDisY = dis[x] + wt。newDisY 小于 dis[y] 的当前值,则找到了一个更短的路径。此时更新 dis[y] = newDisY,并将 (newDisY, y) 这个新的距离估计值加入最小堆中 。n-1,说明终点不可达,返回 -1 。该算法的巧妙之处在于,它通过图构建阶段的“加边”操作,将节点的“反转”机会直接融入了图的结构中。
2*w 的“反转边”(从 v 到 u),这在逻辑上就等价于:在到达节点 v 后,使用了它的特殊操作,反转了边 u->v,并立即移动到了 u。n,E 是原始有向图的边数。在算法构建的邻接表中,边的数量约为 2 * E。Push)和删除最小元(Pop)操作的时间复杂度均为 O(log V)。在最坏情况下,每条边都可能引发一次堆操作(插入新距离),因此总的时间复杂度为 O((V + E) log V) 。dis。g。因为每条原始边被表示为两条边,所以邻接表的总空间是 O(E)。您提供的代码通过扩展图结构来建模节点特有的“边反转”操作,并成功运用了标准的Dijkstra算法和最小堆来高效求解单源最短路径问题。其时间复杂度和空间复杂度在图算法中属于高效范畴,能够处理题目中给出的数据规模(n <= 50000, edges.length <= 100000)。
.
package main
import (
"container/heap"
"fmt"
"math"
)
func minCost(n int, edges [][]int)int {
type edge struct{ to, wt int }
g := make([][]edge, n) // 邻接表
for _, e := range edges {
x, y, wt := e[0], e[1], e[2]
g[x] = append(g[x], edge{y, wt})
g[y] = append(g[y], edge{x, wt * 2}) // 反转边
}
dis := make([]int, n)
for i := range dis {
dis[i] = math.MaxInt
}
dis[0] = 0// 起点到自己的距离是 0
// 堆中保存 (起点到节点 x 的最短路长度,节点 x)
h := &hp{{}}
for h.Len() > 0 {
p := heap.Pop(h).(pair)
disX, x := p.dis, p.x
if disX > dis[x] { // x 之前出堆过
continue
}
if x == n-1 { // 到达终点
return disX
}
for _, e := range g[x] {
y := e.to
newDisY := disX + e.wt
if newDisY < dis[y] {
dis[y] = newDisY // 更新 x 的邻居的最短路
// 懒更新堆:只插入数据,不更新堆中数据
// 相同节点可能有多个不同的 newDisY,除了最小的 newDisY,其余值都会触发上面的 continue
heap.Push(h, pair{newDisY, y})
}
}
}
return-1
}
type pair struct{ dis, x int }
type hp []pair
func (h hp) Len() int { returnlen(h) }
func (h hp) Less(i, j int) bool { return h[i].dis < h[j].dis }
func (h hp) Swap(i, j int) { h[i], h[j] = h[j], h[i] }
func (h *hp) Push(v any) { *h = append(*h, v.(pair)) }
func (h *hp) Pop() (v any) { a := *h; *h, v = a[:len(a)-1], a[len(a)-1]; return }
func main() {
n := 4
edges := [][]int{{0, 2, 1}, {2, 1, 1}, {1, 3, 1}, {2, 3, 3}}
result := minCost(n, edges)
fmt.Println(result)
}

# -*-coding:utf-8-*-
import heapq
from typing import List
def minCost(n: int, edges: List[List[int]]) -> int:
# 邻接表
g = [[] for _ in range(n)]
for x, y, wt in edges:
g[x].append((y, wt)) # 原边
g[y].append((x, wt * 2)) # 反向边,权重翻倍
# 初始化距离数组
dist = [float('inf')] * n
dist[0] = 0
# 最小堆,元素为 (起点到节点x的最短距离, 节点x)
heap = [(0, 0)] # (距离, 节点)
while heap:
dis_x, x = heapq.heappop(heap)
# 如果当前距离大于已记录的最短距离,跳过
if dis_x > dist[x]:
continue
# 到达终点
if x == n - 1:
return dis_x
# 遍历邻接节点
for y, wt in g[x]:
new_dis_y = dis_x + wt
if new_dis_y < dist[y]:
dist[y] = new_dis_y
heapq.heappush(heap, (new_dis_y, y))
return-1
if __name__ == "__main__":
n = 4
edges = [[0, 2, 1], [2, 1, 1], [1, 3, 1], [2, 3, 3]]
result = minCost(n, edges)
print(result)
#include <iostream>
#include <vector>
#include <queue>
#include <climits>
#include <functional>
using namespace std;
int minCost(int n, vector<vector<int>>& edges) {
// 邻接表:存储 (邻居节点, 权重)
vector<vector<pair<int, int>>> g(n);
for (const auto& e : edges) {
int x = e[0], y = e[1], wt = e[2];
g[x].emplace_back(y, wt); // 原边
g[y].emplace_back(x, wt * 2); // 反向边,权重翻倍
}
// 初始化距离数组
vector<int> dist(n, INT_MAX);
dist[0] = 0;
// 最小堆:元素为 (起点到节点x的最短距离, 节点x)
// 使用 greater 使 priority_queue 成为最小堆
priority_queue<pair<int, int>, vector<pair<int, int>>, greater<pair<int, int>>> pq;
pq.emplace(0, 0); // (距离, 节点)
while (!pq.empty()) {
auto [dis_x, x] = pq.top();
pq.pop();
// 如果当前距离大于已记录的最短距离,跳过
if (dis_x > dist[x]) {
continue;
}
// 到达终点
if (x == n - 1) {
return dis_x;
}
// 遍历邻接节点
for (const auto& [y, wt] : g[x]) {
int new_dis_y = dis_x + wt;
if (new_dis_y < dist[y]) {
dist[y] = new_dis_y;
pq.emplace(new_dis_y, y);
}
}
}
return-1;
}
int main() {
int n = 4;
vector<vector<int>> edges = {{0, 2, 1}, {2, 1, 1}, {1, 3, 1}, {2, 3, 3}};
int result = minCost(n, edges);
cout << result << endl;
return0;
}

我们相信人工智能为普通人提供了一种“增强工具”,并致力于分享全方位的AI知识。在这里,您可以找到最新的AI科普文章、工具评测、提升效率的秘籍以及行业洞察。