题目:
你有一台电脑,它可以 同时 运行无数个任务。给你一个二维整数数组 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注意这里选出的时间点可以是不连续的。然后问所有时间点都选出之后,总的最少时间是多少?
理解了题意之后,就思考一下应该怎么做。我觉得很容易想到的一点就是:既然要想最终的时间点最少,那就应该尽可能多的让时间点重叠。
好的,那怎么才能让时间点重叠呢?这里的考虑是,尽可能多的让两个时间段重叠,然后在重叠的时间段里面选时间点。
- 那首先应该对task进行排序,可以根据以往贪心的规律按照结束时间从小到大排序,然后遍历排序后的task。
- 对于遍历到的task[i],首先将task[i][2]删去task[i][0]到task[i][1]这段闭区间里已经作为时间点的数量。
- 如果删去后task[i][2] < 0 ,说明之前选中的时间点已经满足当前区间里时间点的数量,不需要再进行选择了。
- 如果删去后task[i][2] > 0 , 说明当前区间删去之前选中的时间点后,仍需要继续选出task[i][2]个时间点。那选择的策略是什么呢?因为我们是按照结束时间从小到大排序的,所以如果前面的区间跟当前区间有交集的话,肯定是前一段区间的末尾和当前区间的开始有交集,所以我们选择时间点的策略就是从每一段区间的末尾开始选择。这样既可以保证不和前面的交集区间重叠,还能尽可能地和后面区间有交集。注意题目里说的是可以不连续,所以我们可以这样选择。
- 一直遍历到末尾就可以了,输出最终的答案。
代码:
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;
}
};