1376. 通知所有员工所需的时间
难度中等125
公司里有 n
名员工,每个员工的 ID 都是独一无二的,编号从 0
到 n - 1
。公司的总负责人通过 headID
进行标识。
在 manager
数组中,每个员工都有一个直属负责人,其中 manager[i]
是第 i
名员工的直属负责人。对于总负责人,manager[headID] = -1
。题目保证从属关系可以用树结构显示。
公司总负责人想要向公司所有员工通告一条紧急消息。他将会首先通知他的直属下属们,然后由这些下属通知他们的下属,直到所有的员工都得知这条紧急消息。
第 i
名员工需要 informTime[i]
分钟来通知它的所有直属下属(也就是说在 informTime[i]
分钟后,他的所有直属下属都可以开始传播这一消息)。
返回通知所有员工这一紧急消息所需要的 分钟数 。
示例 1:
输入:n = 1, headID = 0, manager = [-1], informTime = [0]
输出:0
解释:公司总负责人是该公司的唯一一名员工。
示例 2:
输入:n = 6, headID = 2, manager = [2,2,-1,2,2,2], informTime = [0,0,1,0,0,0]
输出:1
解释:id = 2 的员工是公司的总负责人,也是其他所有员工的直属负责人,他需要 1 分钟来通知所有员工。
上图显示了公司员工的树结构。
提示:
1 <= n <= 10^5
0 <= headID < n
manager.length == n
0 <= manager[i] < n
manager[headID] == -1
informTime.length == n
0 <= informTime[i] <= 1000
- 如果员工
i
没有下属,informTime[i] == 0
。 - 题目 保证 所有员工都可以收到通知。
DFS(自顶向下)
class Solution {
List<Integer>[] g;
int[] informTime;
public int numOfMinutes(int n, int headID, int[] manager, int[] informTime) {
g = new ArrayList[n];
this.informTime = informTime;
Arrays.setAll(g, e -> new ArrayList<>());
for(int i = 0; i < n; i++){
if(i == headID) continue;
g[manager[i]].add(i);
}
return dfs(headID);
}
public int dfs(int id){
int time = 0;
for(int y : g[id]){
time = Math.max(time, dfs(y));
}
return informTime[id] + time;
}
}
python
class Solution:
def numOfMinutes(self, n: int, headID: int, manager: List[int], informTime: List[int]) -> int:
g = [[] for _ in range(n)]
for i, m in enumerate(manager):
if m >= 0:
g[m].append(i) # 建树
def dfs(x: int) -> int:
max_path_sum = 0
for y in g[x]:
max_path_sum = max(max_path_sum, dfs(y))
return max_path_sum + informTime[x]
return dfs(headID)
自底向上
题解:https://leetcode.cn/problems/time-needed-to-inform-all-employees/solution/shen-ru-li-jie-di-gui-zi-ding-xiang-xia-ps0mm/
由于 manager
数组中保存了每个节点的父节点,无需建树,直接顺着父节点,一路向上,同时累加路径上的 informTime[x]
。
如果暴力枚举每个点,取所有累加值中的最大值作为答案,时间复杂度是O(n^2)
的,如何优化?
使用 记忆化搜索 这一思想,把从 x
向上得到的累加值记录到一个 memo
数组中,如果下次再递归到 x
,就直接返回 memo
数组中保存的累加值。
class Solution {
int[] manager, informTime;
int[] cache;
public int numOfMinutes(int n, int headID, int[] manager, int[] informTime) {
this.manager = manager;
this.informTime = informTime;
cache = new int[n];
Arrays.fill(cache, -1);
int ans = 0;
for(int i = 0; i < n; i++){
ans = Math.max(ans, dfs(i));
}
return ans;
}
public int dfs(int idx){
if(manager[idx] < 0) // 到达顶点-1
return informTime[idx];
if(cache[idx] >= 0) return cache[idx];
return cache[idx] = dfs(manager[idx]) + informTime[idx];
}
}
空间优化:
把计算结果直接保存到 informTime
中。
如何判断之前是否计算过呢?利用 manager
数组,如果 x
计算过,就把 manager[x]
置为 −1
。
class Solution {
public int numOfMinutes(int n, int headID, int[] manager, int[] informTime) {
int ans = 0;
for(int i = 0; i < n; i++){
ans = Math.max(ans, dfs(manager, informTime, i));
}
return ans;
}
public int dfs(int[] manager, int[] informTime, int x){
if(manager[x]>= 0){
informTime[x] += dfs(manager, informTime, manager[x]);
manager[x] = -1;
}
return informTime[x];
}
}