区间最佳问题

问题描述:

问题:鸡窝托斯魅魔的高效陪玩计划

作为鸡窝托斯魅魔,你有 n 个可爱的学生盼着和你玩耍,但你身为 “大忙人”(要处理各种工作),必须用 最少的总时间 满足所有学生的需求。

  1. 每个学生有专属的「空闲时间区间」[start, end]:只能在这个时间段内陪他玩(早了学生在写作业,晚了学生要睡觉,学生也得作息规律~);
  2. 每个学生有「最低陪玩时长」time:你陪他玩的总时间累计必须达到 time(可以分段陪,不用一次性连续,但总时长不能少);
  3. 你的关键限制:同一时间可以陪无数个学生玩;
  4. 你的目标:找到一种陪玩时间安排,让你 投入的总时间最少

问题转换:

要在一个时间区间内能与最多的学生玩耍(贪心)

  1. 将每个学生回家的时间按照升序排列。(排序)
  2. 让每个学生回家的时间尽量晚,便于与其他学生安排在一起。(贪心)
class Solution {
public:
    // 场景:安排陪玩时间,用最少总时间满足所有学生需求
    int minimumPlayTime(vector<vector<int>>& students) {
        // ① 按学生空闲区间的结束时间升序排序(优先陪早结束的学生,避免错过她的空闲时间)
        ranges::sort(students, {}, [](auto& cmp) { return cmp[1]; });
        
        int totalPlayTime = 0; // 总陪玩时间(你要投入的最少总时间)
        // 时间轴:run[time] = 1 表示这个时间点正在陪学生玩
        vector<int> run(students.back()[1] + 1);
        
        for (auto& student : students) {
            int freeStart = student[0];  // 学生空闲开始时间
            int freeEnd = student[1];    // 学生空闲结束时间
            int needPlay = student[2];   // 学生需要的最低陪玩时长
            
            // 计算已重叠的陪玩时间:这个学生的空闲区间内,你已经在陪别人玩的时间(可复用)
            needPlay -= reduce(run.begin() + freeStart, run.begin() + freeEnd + 1);
            
            // ② 还有需要补充的陪玩时间:从学生空闲结束时间往前安排(最大化后续重叠)
            while (needPlay > 0) {
                if (!run[freeEnd]) { // 这个时间点还没安排陪玩
                    run[freeEnd] = 1; // 标记为正在陪玩
                    needPlay--;       // 剩余需要的陪玩时长减少
                    totalPlayTime++;  // 总陪玩时间增加
                }
                freeEnd--; // 往前找未安排的时间点
            }
        }
        
        return totalPlayTime;
    }
};

时间复杂度 O(Nlogn)

空间复杂度 O(N)

力扣题源

2589. 完成所有任务的最少时间 - 力扣(LeetCode)

// 题目总结:在一个区间内执行的任务最多 任务执行的重叠度最高
// ① 排序: 按照任务结束时间升序排序。
// ② 贪心: 让每个任务都尽可能的结束的晚 这样下一个任务与当前任务会有更多的重叠时间。
class Solution {
public:
    int findMinimumTime(vector<vector<int>>& tasks) {
        ranges::sort(tasks, {}, [](auto& cmp) {return cmp[1];}); // ① 按照end排序
        int ans = 0;
        vector<int> run(tasks.back()[1] + 1);
        for (auto& task : tasks) {
            int start = task[0], end = task[1], time = task[2];
            // 另外所需的执行时间 = 总的花费时间 - 重叠的时间区域(我和其他任务一起执行)
            time -= reduce(run.begin() + start, run.begin() + end + 1);
            // 存在另外花费的时间 ② 我就从结束时间开始往前执行
            while (time > 0) {
                if(!run[end]) {
                    run[end] = 1; // 标记这个时间已经在执行了(便于前面重叠区域的计算)
                    time--;
                    ans++; // 另外花费的时间
                }
                end--;
            }
        }
        return ans;
    }
};

思考:这个算法还能怎么优化?

tips:栈 + 二分

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值