LeetCode第 630 题:课程表 III(C++)

630. 课程表 III - 力扣(LeetCode)
在这里插入图片描述

其实和前面的课程表题目不同,这题应该和拓扑排序没啥关系,应该属于贪心算法的区间覆盖问题:数据结构与算法:37 | 贪心算法:贪心算法实现Huffman压缩编码_zj-CSDN博客

区间覆盖问题里,贪心地选左端点跟前面的已经覆盖的区间不重合的(限制条件),右端点又尽量小的。

限制值是截止日期,期望值就是课程的门数,选谁都是一门课嘛,一开始我想的是尽量选择截止日期早的,不过想想不太合理。写出来也确实:

class Solution {
public:
    int scheduleCourse(vector<vector<int>>& courses) {
        //按截止时间排序,如果截至时间相同,持续时间短的放前面
        sort(courses.begin(), courses.end(), [](const vector<int> &a, const vector<int> &b){
            return a[1] == b[1] ? a[0] < b[0] : a[1] < b[1];
        });
        int res = 0, sum = 0;
        for(const auto &v : courses){
            if(sum + v[0] <= v[1]){
                ++res;
                sum += v[0];
            }
        }
        return res;
    }
};

在这个用例出错了:

[[5,5],[4,6],[2,6]]

预期 :2,输出:1

按照上面的的策略,优先选择[5,5],然后剩下的两个都不能选了。但是如果我优先选择[2,6]的话,[4,6]也刚好能选,能够修两门课,是更好的选择。

来分析一下为什么会错,因为真正的右端点并不是数组里的v[1],v[1]是截止时间,而不是真正的课程结束时间。比如我一开始选择[2,6],那么这门课程在时间点为0+2 = 2的时候就结束了,此时我就可以选择下一门课了,而不需要等到这门课的截至日期6的时候。

正确思路是,还是之前的排序方法,先按照截至日期排序,这是符合常理的,截至日期更近的,一般需要尽快选择。所以还是上面的那个例子,我们一开始还是选择[5, 5],而当进行第二个选择的时候,下一个数据是[2,6](由于排序的关系,后面的选择截至日期总是更靠后的),这时候我们发现:

  • 选了[5, 5]之后,[2, 6]不能选择了(0+5+2 > 6),也就是这两者不能共存。但是我们可以直接抛弃[2, 6]吗?
  • 可以看到选择[5, 5]的持续时间5 > 选择[2, 6]的持续时间2,也就是同样是一门课,选择[2, 6]可以更早的结束,那不就意味着为后续的选择留下更多的时间吗?所以[2, 6]是比[5, 5]更好的选择,我们就可以用[2, 6]替换[5, 5]。

所以总体思路就像贪心+动态选择

使用优先级队列方便处理:

class Solution {
public:
    struct cmp{
        bool operator()(const int a, const int b){//重载函数调用运算符
            return a < b;
        }
    };
    int scheduleCourse(vector<vector<int>>& courses) {
        sort(courses.begin(), courses.end(), [](const vector<int> &a, const vector<int> &b){
            return a[1] == b[1] ? a[0] < b[0] : a[1] < b[1];
        });
        priority_queue<int, vector<int>, cmp> q;//这样也行
        //priority_queue<int, vector<int>, less<int>> q;//默认就是大顶堆,所以用下一行的也行
        //priority_queue<int> q;
        int sum = 0;
        for(const auto &v : courses){
            if(sum + v[0] <= v[1]){//可选,并不互斥
                sum += v[0];
                q.push(v[0]);
            }else if(!q.empty() && q.top() > v[0]){//当前v是更好的选择,可以进行替换
                sum += v[0] - q.top();
                q.pop();
                q.push(v[0]);
            }
        }
        return q.size();
    }
};
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值