个人技术分享

LeetCode-2589. 完成所有任务的最少时间【栈 贪心 数组 二分查找 排序】

题目描述:

你有一台电脑,它可以 同时 运行无数个任务。给你一个二维整数数组 tasks ,其中 tasks[i] = [starti, endi, durationi] 表示第 i 个任务需要在 闭区间 时间段 [starti, endi] 内运行 durationi 个整数时间点(但不需要连续)。

当电脑需要运行任务时,你可以打开电脑,如果空闲时,你可以将电脑关闭。

请你返回完成所有任务的情况下,电脑最少需要运行多少秒。

示例 1:

输入:tasks = [[2,3,1],[4,5,1],[1,5,2]]
输出:2
解释:

  • 第一个任务在闭区间 [2, 2] 运行。
  • 第二个任务在闭区间 [5, 5] 运行。
  • 第三个任务在闭区间 [2, 2] 和 [5, 5] 运行。
    电脑总共运行 2 个整数时间点。

示例 2:

输入:tasks = [[1,3,2],[2,5,3],[5,6,2]]
输出:4
解释:

  • 第一个任务在闭区间 [2, 3] 运行
  • 第二个任务在闭区间 [2, 3] 和 [5, 5] 运行。
  • 第三个任务在闭区间 [5, 6] 运行。
    电脑总共运行 4 个整数时间点。

提示:

1 <= tasks.length <= 2000
tasks[i].length == 3
1 <= starti, endi <= 2000
1 <= durationi <= endi - starti + 1

解题思路一:贪心+暴力

三个关键点。1. 按区间右端点排序。2. 把任务尽可能安排在区间的后缀。3. 用数组记录任务的执行情况
在这里插入图片描述

class Solution:
    def findMinimumTime(self, tasks: List[List[int]]) -> int:
        tasks.sort(key = lambda x: x[1]) # 排序
        run = [False] * (tasks[-1][1] + 1) # 初始化确定哪些时间运行的数组
        for s, e, d in tasks:
            d -= sum(run[s: e + 1])  # 去掉运行中的时间点
            if d <= 0:  # 该任务已完成
                continue
            for i in range(e, s-1, -1): # 标记需要运行的时间点
                if run[i]:
                    continue
                run[i] = True
                d -= 1
                if d == 0:
                    break
        return sum(run)

时间复杂度:O(nM)其中 n 是 tasks 的大小,M 是 tasks 的时间段右端点 end 的最大值。
空间复杂度:O(logn + M) 排序和记录的数组

解题思路二:栈+二分查找

在这里插入图片描述

class Solution:
    def findMinimumTime(self, tasks: List[List[int]]) -> int:
        tasks.sort(key=lambda t: t[1])
        # 栈中保存闭区间左右端点,栈底到栈顶的区间长度的和
        st = [(-2, -2, 0)]  # 哨兵,保证不和任何区间相交
        for start, end, d in tasks:
            _, r, s = st[bisect_left(st, (start,)) - 1]
            d -= st[-1][2] - s  # 去掉运行中的时间点
            if start <= r:  # start 在区间 st[i] 内
                d -= r - start + 1  # 去掉运行中的时间点
            if d <= 0:
                continue
            while end - st[-1][1] <= d:  # 剩余的 d 填充区间后缀
                l, r, _ = st.pop()
                d += r - l + 1  # 合并区间
            st.append((end - d + 1, end, st[-1][2] + d))
        return st[-1][2]

时间复杂度:O(nlogn)
空间复杂度:O(n)

解题思路三:简化版

class Solution:
    def findMinimumTime(self, tasks: List[List[int]]) -> int:
        tasks.sort(key = lambda x: x[1])
        run = [False] * (tasks[-1][1] + 1)
        for s, e, d in tasks:
            d -= sum(run[s: e + 1]) # 先减去已经运行的时间

            if d <= 0: # 已经ok
                continue
            
            for i in range(e, s - 1, -1): # 还没ok
                if run[i]:
                    continue
                run[i] = True
                d -= 1
                if d == 0:
                    break

        return sum(run)

时间复杂度:O(nM)其中 n 是 tasks 的大小,M 是 tasks 的时间段右端点 end 的最大值。
空间复杂度:O(logn + M) 排序和记录的数组


创作不易,观众老爷们请留步… 动起可爱的小手,点个赞再走呗 (๑◕ܫ←๑)
欢迎大家关注笔者,你的关注是我持续更博的最大动力


原创文章,转载告知,盗版必究



在这里插入图片描述


在这里插入图片描述
♠ ⊕ ♠ ⊕ ♠ ⊕ ♠ ⊕ ♠ ⊕ ♠ ⊕ ♠ ⊕ ♠ ⊕ ♠ ⊕ ♠ ⊕ ♠ ⊕ ♠ ⊕ ♠ ⊕ ♠ ⊕ ♠ ⊕ ♠ ⊕ ♠ ⊕ ♠ ⊕ ♠ ⊕ ♠ ⊕ ♠ ⊕ ♠ ⊕ ♠ ⊕ ♠ ⊕ ♠ ⊕ ♠ ⊕ ♠ ⊕ ♠ ⊕ ♠ ⊕ ♠ ⊕ ♠