一、题目
1、题目描述
你有一台电脑,它可以 同时 运行无数个任务。给你一个二维整数数组
tasks
,其中tasks[i] = [starti, endi, durationi]
表示第i
个任务需要在 闭区间 时间段[starti, endi]
内运行durationi
个整数时间点(但不需要连续)。当电脑需要运行任务时,你可以打开电脑,如果空闲时,你可以将电脑关闭。
请你返回完成所有任务的情况下,电脑最少需要运行多少秒。
2、接口描述
python3
class Solution:def findMinimumTime(self, tasks: List[List[int]]) -> int:
cpp
class Solution {
public:int findMinimumTime(vector<vector<int>>& tasks) {}
};
3、原题链接
2589. 完成所有任务的最少时间
二、解题报告
1、思路分析
贪心问题中的经典问题——区间选点问题
区间选点问题在基础算法,贪心算法,贪心策略,OJ练习-CSDN博客中有介绍
其实就是若干区间[l, r],每个区间都有一个权值
我们要在数轴上选一些点使得每个区间内的点的数目都不得少于其权值
问我们最少要选多少点?
朴素O(nlogn + nU)做法:按照区间右端点升序排序,然后每个区间贪心的从右往左选点
这个做法对于每个区间都要遍历一遍,考虑区间操作可以用线段树优化我们可以优化到O(nlogn + nlogU)
栈上二分可以进一步优化到O(nlogn):
仍然是区间按右端点升序
我们选的点构成了若干不相交区间,我们在栈上按照左端点升序保存区间左端点,区间长度,区间长度前缀和
每次遍历到的区间[l, r, len]先在栈上二分第一个左端点大于等于 l 的区间[L, R]
先减去左边区间的贡献,再计算[L, R]对[l, r]的贡献
然后剩下的部分优先从r往左边放不断和栈顶的区间合并即可
其实和朴素做法是一样的,无论是线段树优化还是栈上二分都是优化了区间操作罢了
线段树太长了下面不写了
2、复杂度
时间复杂度: F1:O(nlogn + nU) F2:O(nlogn)空间复杂度:O(n)
3、代码详解
F1
python3
class Solution:def findMinimumTime(self, tasks: List[List[int]]) -> int:vis = [0] * 2001for l, r, sz in sorted(tasks, key=lambda x: x[1]):sz -= sum(vis[l:r+1:])if sz > 0:for i in range(r, l - 1, -1):if not vis[i]:vis[i] = 1sz -= 1if not sz:breakreturn sum(vis)
cpp
class Solution {
public:int findMinimumTime(vector<vector<int>>& tasks) {vector<bool> vis(2001);sort(tasks.begin(), tasks.end(), [](const vector<int>& x, const vector<int>& y){return x[1] < y[1];});for(auto& t : tasks) {int l = t[0], r = t[1], len = t[2];len -= accumulate(vis.begin() + l, vis.begin() + r + 1, 0);if (len > 0)for(; len; r --)if (!vis[r])len -= (vis[r] = 1);}return accumulate(vis.begin(), vis.end(), 0);}
};
F2
python3
class Solution:def findMinimumTime(self, tasks: List[List[int]]) -> int:st = [(-1, -1, 0)]for l, r, sz in sorted(tasks, key=lambda x: x[1]):_, R, s = st[bisect_left(st, (l,)) - 1]sz -= st[-1][2] - sif l <= R:sz -= R - l + 1if sz <= 0:continuewhile r - st[-1][1] <= sz:L, R, _ = st.pop()sz += R - L + 1st.append((r - sz + 1, r, st[-1][2] + sz))return st[-1][2]
cpp
class Solution {
public:int findMinimumTime(vector<vector<int>>& tasks) {int n = tasks.size();vector<array<int, 3>> st;st.reserve(n);st.push_back({-1, -1, 0});sort(tasks.begin(), tasks.end(), [](const vector<int>& x, const vector<int>& y){return x[1] < y[1];});for (auto& p : tasks) {int l = p[0], r = p[1], len = p[2];auto [L, R, s] = *-- lower_bound(st.begin(), st.end(), l, [](const auto& a, int x){return a[0] < x;});len -= st.back()[2] - s;if (l <= R) len -= R - l + 1;if (len <= 0) continue;while (r - st.back()[1] <= len) {auto [L, R, _] = st.back();st.pop_back();len += R - L + 1;}st.push_back({r - len + 1, r, st.back()[2] + len});}return st.back()[2];}
};