2589. 完成所有任务的最少时间

28 篇文章 0 订阅
12 篇文章 0 订阅

题目
你有一台电脑,它可以 同时 运行无数个任务。给你一个二维整数数组 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

原题链接:

https://leetcode.cn/problems/minimum-time-to-complete-all-tasks/description/?envType=daily-question&envId=2024-05-15

思路:

首先理解题意,给出一个二维数组,其中 tasks[i] = [starti, endi, durationi] 表示第 i 个任务需要在 闭区间 时间段 [starti, endi] 内运行 durationi 个整数时间点。

也就是说要在每一个task[i][0]到task[i][1]之间的闭区间里选出durationi个整数时间点,且要i注意这里选出的时间点可以是不连续的。然后问所有时间点都选出之后,总的最少时间是多少?

理解了题意之后,就思考一下应该怎么做。我觉得很容易想到的一点就是:既然要想最终的时间点最少,那就应该尽可能多的让时间点重叠

好的,那怎么才能让时间点重叠呢?这里的考虑是,尽可能多的让两个时间段重叠,然后在重叠的时间段里面选时间点

  1. 那首先应该对task进行排序,可以根据以往贪心的规律按照结束时间从小到大排序,然后遍历排序后的task。
  2. 对于遍历到的task[i],首先将task[i][2]删去task[i][0]到task[i][1]这段闭区间里已经作为时间点的数量。
  3. 如果删去后task[i][2] < 0 ,说明之前选中的时间点已经满足当前区间里时间点的数量,不需要再进行选择了。
  4. 如果删去后task[i][2] > 0 , 说明当前区间删去之前选中的时间点后,仍需要继续选出task[i][2]个时间点。那选择的策略是什么呢?因为我们是按照结束时间从小到大排序的,所以如果前面的区间跟当前区间有交集的话,肯定是前一段区间的末尾和当前区间的开始有交集,所以我们选择时间点的策略就是从每一段区间的末尾开始选择。这样既可以保证不和前面的交集区间重叠,还能尽可能地和后面区间有交集。注意题目里说的是可以不连续,所以我们可以这样选择。
  5. 一直遍历到末尾就可以了,输出最终的答案。

代码:

class Solution {
public:
    int findMinimumTime(vector<vector<int>>& tasks) {
        ranges::sort(tasks,[](auto &a , auto &b){ return a[1] < b[1];});
        //按照结束时间tasks[1]从小到大进行排序
        int res = 0;
        vector<int> flag(tasks.back()[1]+1);
        //声明一个标记数组flag, 范围是最大的结束时间+1
        for(auto& t : tasks){
            int start = t[0];
            int end = t[1];
            int d = t[2];
            //遍历tasks, start是开始时间,end是结束时间,d是要选出的时间点的数量
            d -= reduce(flag.begin()+start , flag.begin() + end + 1);
            //reduce是计算start到end这段区间和,d-=reduce就是d减去start到end这段区间中已经被选为时间点的数量
            for(int i = end ; d > 0 ; i--){
                //从end结束时间开始向前走d个长度,将flag标记为时间点,标记一个时间点,res++
                if(!flag[i]){
                    flag[i] = true;
                    d--;
                    res++;
                }
            }
        }
        return res;
    }
};
  • 5
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值