LC每日一题|20240515 - 2589. 完成所有任务的最少时间

224 阅读2分钟

LC每日一题|20240515 - 2589. 完成所有任务的最少时间

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

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

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

提示:

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

题目等级:Hard

题目评分:2380.60

解题思路

反贪局!

拿到这题我的第一反应是贪心,先把时间点按照「贡献」排序,然后一个个的弹出来去和tasks去匹,最热门的时间用来执行,性价比看起来是最高的了~

但是这样可能会带来两个问题:

  • 复杂度蹭蹭的往上窜
  • 这样做怎么确保全局最优?

坦白讲有点不敢写了23333

后来还是看了看题目下的评论,有位老哥举了个拖延症的例子...

豁然开朗!

我们可以先把tasks对endTime排序,保证前一个task的endTime小于等于后一个task的endTime。

这样对于后一个task来说,前一个task越「拖延」,对于它是越「有利」的。因为前边的执行时间越靠后,越有可能与自己的执行区间重合。

然后我们开一个数组用于记录「该时间点电脑是否打开」,遍历排序后的tasks并按照下列规则维护时间表:

  • 遍历区间[task[0], task1[1]],如果某一时间已经被前边的任务设定为「打开」,则 task[2]-- ,代表该task在此刻也执行了
  • 如果跑下来之后 task[2] > 0,则说明现在「打开」的时间段是不够的,必须要再开一些时间。这时我们可以倒序遍历时间区间去寻找尚处于「关闭」的时间点「打开」,也就是上文所谓的 拖延

跑完之后数一下时间数组内有多少「打开」的时间点就可以了。

AC代码

class Solution {
    fun findMinimumTime(tasks: Array<IntArray>): Int {
        val time = BooleanArray(2001)
        tasks.sortBy { it[1] }
        for (task in tasks) {
            for (i in task[0]..task[1]) {
                if (time[i]) task[2]--
            }
            while (task[2] > 0) {
                for (i in task[1] downTo task[0]) {
                    if (!time[i]) {
                        time[i] = true
                        task[2]--
                    }
                    if (task[2] == 0) break
                }
            }
        }
        return time.filter { it }.size 
    }
}

时间复杂度:O(n^2)

空间复杂度:O(n)