方法一:贪心 + 深度优先搜索
思路与算法:题目等价于给出了一颗以节点 0 0 0 为根节点的树,并且初始树上的每一个节点上都有一个人,现在所有人都需要通过「车子」向结点 0 0 0 移动。
对于某一个节点 x x x, x ≠ 0 x \neq 0 x=0,其父节点为 y y y。因为以节点 x x x 为根结点的子树上的人都需要通过边 x → y x \rightarrow y x→y 向节点 0 0 0 移动,所以为了使这条边上的「车子」利用率最高,我们贪心的让 x x x 的全部子节点上的人到了节点 x x x 后再一起坐车向上移动,我们不妨设以节点 x x x 为根节点的子树大小为 cnt x \text{cnt}_x cntx,那么我们至少需要「车子」的数量为 ⌈ cnt x seats ⌉ \lceil \frac{\textit{cnt}_x}{\textit{seats}} \rceil ⌈seatscntx⌉,其中 seats \textit{seats} seats 为一辆车的给定座位数。
那么我们可以通过从根结点 0 0 0 往下进行「深度优先搜索」,每一条边上「车子」的数目即为该条边上汽油的开销,统计全部边上汽油的开销即为最终答案。
代码:
class Solution {
long res = 0;
public long minimumFuelCost(int[][] roads, int seats) {
int n = roads.length;
List<Integer>[] graph = new List[n + 1];
for(int i = 0; i <= n; i++){
graph[i] = new ArrayList<Integer>();
}
for(int[] edge : roads){ //构造图
graph[edge[0]].add(edge[1]);
graph[edge[1]].add(edge[0]);
}
dfs(0, -1, seats, graph); //从节点0开始
return res;
}
public int dfs(int cur, int father, int seats, List<Integer>[] graph){
int peopleSum = 1;
for(int child : graph[cur]){
if(child != father){ //排除父节点
int peopleCnt = dfs(child, cur, seats, graph);
peopleSum += peopleCnt;
res += (peopleCnt + seats - 1) / seats; //到达每个节点所需代价
}
}
return peopleSum;
}
}
值得学习的点:
- 只遍历子节点的做法
if(child != father){...}
。 - 计算到达每个节点需要的代价
res += (peopleCnt + seats - 1) / seats
,向上取整的做法十分巧妙。