Description
有一棵以節點
0為首都的樹,每條邊代表一條道路。每個非首都節點有一位代表需要前往首都。每輛車可載seats人,行駛一條邊消耗一單位燃油。代表可以在節點共乘。求所有代表到達首都的最小總燃油消耗。
Example:
Input: roads = [[0,1],[0,2],[0,3]], seats = 5 Output: 3
Intuition#
從葉子往根匯聚,每條邊需要的車輛數 = ceil(經過人數 / seats)。
- 這是一棵樹,每個人最終都要到節點 0
- 每條邊上通過的人數等於該邊子樹的節點數
- 每條邊的燃油消耗 = ceil(通過人數 / seats)
Approaches#
⭐1: DFS(從根向下遞迴)#
- 概念: DFS 計算每個子樹的節點數,每條邊的燃油消耗為 ceil(子樹人數 / seats)
- 時間複雜度:
O(n) - 空間複雜度:
O(n)
class Solution {
fun minimumFuelCost(roads: Array<IntArray>, seats: Int): Long {
val n = roads.size + 1
val graph = Array(n) { mutableListOf<Int>() }
for ((u, v) in roads) {
graph[u].add(v)
graph[v].add(u)
}
var fuel = 0L
fun dfs(node: Int, parent: Int): Long {
var people = 1L
for (next in graph[node]) {
if (next != parent) {
people += dfs(next, node)
}
}
if (node != 0) {
fuel += (people + seats - 1) / seats
}
return people
}
dfs(0, -1)
return fuel
}
}2: BFS(拓撲排序從葉子到根)#
- 概念: 從葉子節點開始 BFS,每次處理葉子時將人數傳遞給父節點
- 時間複雜度:
O(n) - 空間複雜度:
O(n)
class Solution {
fun minimumFuelCost(roads: Array<IntArray>, seats: Int): Long {
val n = roads.size + 1
if (n == 1) return 0
val graph = Array(n) { mutableListOf<Int>() }
val degree = IntArray(n)
for ((u, v) in roads) {
graph[u].add(v)
graph[v].add(u)
degree[u]++
degree[v]++
}
val queue = ArrayDeque<Int>()
val people = LongArray(n) { 1L }
for (i in 1 until n) {
if (degree[i] == 1) queue.add(i)
}
var fuel = 0L
while (queue.isNotEmpty()) {
val node = queue.removeFirst()
fuel += (people[node] + seats - 1) / seats
for (next in graph[node]) {
people[next] += people[node]
degree[next]--
if (degree[next] == 1 && next != 0) {
queue.add(next)
}
}
}
return fuel
}
}🔑 Takeaways#
- Pattern: 樹上貪心 – 從葉子往根匯聚,每條邊獨立計算
- 關鍵技巧:
ceil(a / b)在整數運算中用(a + b - 1) / b;每條邊的燃油獨立計算,不需考慮共乘的路由