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];
    }
}	



















