【2022-11-26每日一题】882. 细分图中的可到达节点[Hard]
2022-11-26
3分钟阅读时长
2022-11-26每日一题:882. 细分图中的可到达节点
难度:Hard
标签:图 、 最短路 、 堆(优先队列)
给你一个无向图(原始图),图中有 n
个节点,编号从 0
到 n - 1
。你决定将图中的每条边 细分 为一条节点链,每条边之间的新节点数各不相同。
图用由边组成的二维数组 edges
表示,其中 edges[i] = [ui, vi, cnti]
表示原始图中节点 ui
和 vi
之间存在一条边,cnti
是将边 细分 后的新节点总数。注意,cnti == 0
表示边不可细分。
要 细分 边 [ui, vi]
,需要将其替换为 (cnti + 1)
条新边,和 cnti
个新节点。新节点为 x1
, x2
, ..., xcnti
,新边为 [ui, x1]
, [x1, x2]
, [x2, x3]
, ..., [xcnti+1, xcnti]
, [xcnti, vi]
。
现在得到一个 新的细分图 ,请你计算从节点 0
出发,可以到达多少个节点?如果节点间距离是 maxMoves
或更少,则视为 可以到达 。
给你原始图和 maxMoves
,返回 新的细分图中从节点 0
出发 可到达的节点数 。
示例 1:
输入:edges = [[0,1,10],[0,2,1],[1,2,2]], maxMoves = 6, n = 3 输出:13 解释:边的细分情况如上图所示。 可以到达的节点已经用黄色标注出来。
示例 2:
输入:edges = [[0,1,4],[1,2,6],[0,2,8],[1,3,1]], maxMoves = 10, n = 4 输出:23
示例 3:
输入:edges = [[1,2,4],[1,4,5],[1,3,1],[2,3,4],[3,4,5]], maxMoves = 17, n = 5 输出:1 解释:节点 0 与图的其余部分没有连通,所以只有节点 0 可以到达。
提示:
0 <= edges.length <= min(n * (n - 1) / 2, 104)
edges[i].length == 3
0 <= ui < vi < n
- 图中 不存在平行边
0 <= cnti <= 104
0 <= maxMoves <= 109
1 <= n <= 3000
方法一:Dijkstra 算法
func reachableNodes(edges [][]int, maxMoves int, n int) (ans int) {
g := make([][]neighbor, n)
// 建图
for _, e := range edges {
u, v, cnt := e[0], e[1], e[2]
g[u] = append(g[u], neighbor{v, cnt+1})
g[v] = append(g[v], neighbor{u, cnt+1})
}
// 从0出发最短路径
dist := dijkstra(g, 0)
for _, d := range dist {
// 这个点可以在 maxMoves 步内到达
if d <= maxMoves {
ans++
}
}
for _, e := range edges {
u, v, cnt := e[0], e[1], e[2]
a := max(0, maxMoves-dist[u])
b := max(0, maxMoves-dist[v])
ans += min(a+b , cnt) // 这条边上可以到达的节点数
}
return ans
}
// 以下为 Dijkstra 算法模板
type neighbor struct{ to, weight int }
func dijkstra(g [][]neighbor, start int) []int {
dist := make([]int, len(g))
for i := range dist {
dist[i] = math.MaxInt32
}
dist[start] = 0
h := hp{{start, 0}}
for len(h) > 0 {
p := heap.Pop(&h).(pair)
x := p.x
if dist[x] < p.dist { // 已经计算过
continue
}
for _, e := range g[x] {
y := e.to
if d := dist[x] + e.weight; d < dist[y] {
dist[y] = d
heap.Push(&h, pair{y, d})
}
}
}
return dist
}
// 小顶堆
type pair struct {x, dist int}
type hp []pair
func (h hp) Len() int { return len(h) }
func (h hp) Less(i, j int) bool { return h[i].dist < h[j].dist }
func (h hp) Swap(i, j int) { h[i], h[j] = h[j], h[i] }
func (h *hp) Push(v interface{}) { *h = append(*h, v.(pair)) }
func (h *hp) Pop() (v interface{}) {
a := *h
*h, v = a[:len(a)-1], a[len(a)-1]
return v
}
func max(a, b int) int {
if a > b {
return a
}
return b
}
func min(a, b int) int {
if a < b {
return a
}
return b
}
复杂度分析
时间复杂度:O(n+mlogm),其中 m 为 edges 的长度。注意如果同一个点的最短路被更新多次,堆中会有多个相同点,因此堆中的元素个数是 O(m) 的,每次入堆出堆的时间复杂度为 O(logm)。
空间复杂度:O(n+m)。