算法练习(4)—— 贪心算法

算法练习(4)—— 贪心算法

前言

所谓贪心,就是只奔着目前最好的去,按照当前最优的情况去执行,所以按照贪心算法求出的一定是局部的最优解。注意,有可能求不出全局最优解…不过,如果加以合理的改进,贪心算法是可以求出全局最优解的。
正好在算法课之外听了这个算法的讲解,就打算拿道题目来热热身。

习题

本次题目取自leetcode中的 Greedy 栏目中的第630题:
Course Schedule III


老规矩,上题:

Description

There are n different online courses numbered from 1 to n. Each course has some duration(course length) t and closed on dth day. A course should be taken continuously for t days and must be finished before or on the dth day. You will start at the 1st day.

Given n online courses represented by pairs (t,d), your task is to find the maximal number of courses that can be taken.

Example

Input: [[100, 200], [200, 1300], [1000, 1250], [2000, 3200]]
Output: 3
Explanation:
There’re totally 4 courses, but you can take 3 courses at most:
First, take the 1st course, it costs 100 days so you will finish it on the 100th day, and ready to take the next course on the 101st day.
Second, take the 3rd course, it costs 1000 days so you will finish it on the 1100th day, and ready to take the next course on the 1101st day.
Third, take the 2nd course, it costs 200 days so you will finish it on the 1300th day.
The 4th course cannot be taken now, since you will finish it on the 3300th day, which exceeds the closed date.

Note

  1. The integer 1 <= d, t, n <= 10,000.
  2. You can’t take two courses simultaneously

思路与代码

1.首先先理解一下题意。具体就是给你一堆的vector,每个vector有两个参数:
[修完这门课需要的时间, 这门课的截止日期(第d天截止)]
也就是说,如果你预期上完这门课会超出截止日期,那么这门课就与你无缘了。
而且注意,不能同时上2门课!所以你要尽量合理的安排时间,尽量学更多的课程,学习使我快乐:)

2.理解到这里就够了嘛,简直大错特错。我刚看到这个题目第一时间想到的就是按截止日期排序,让截止日期长的尽量排前面,这样就足够贪心了吧。为什么这样的题目还标注为Hard?还真是有理由的..

3.要知道,合理利用时间并不是仅仅的靠着截止日期来排序,能进就进,不能就bye bye。如果这样的话,就会变成我开头说的局部最优。这并不是全局最优。为什么呢?因为vector中还有第一个参数的影响,那就是学时的因素。一个课程学时过长,可能会导致之后的许多课程都上不了,这样还不如不上它!我举个栗子:

[[9, 10], [3, 11], [4, 12], [7, 20]]
如果选用[9, 10], 那么最后的结果是[[9, 10], [7, 20]], 可以选2门课程
如果舍弃[9, 10], 那么最后的结果是[[3, 11], [4, 12], [7, 20]], 可以选3门课程,而且剩余时间很多

造成以上原因的就是第一个课程的学时太长,占用了过多的天数,可能导致其他的课程完全无法进行。

4.所以改进一下算法,先按照2中说的做。当发生当前课程插入不了的问题时,删除掉已选课程中所占学时最多的课程,腾出时间。其实这也是一种贪心,是学时上的贪心。加上之前截止时间上的贪心,这个贪心算法就基本完成了。

5.接下来说说与算法无关的,就是代码实现过程中我遇到的问题。

  • 首先是一开始的排序。既然已经用到了STL,那就没必要自己写排序了,直接拿STL中的快排即可,简单方便。这里贴一个博客大家可以去看看:http://blog.csdn.net/bz201/article/details/543001
    记得如果比较函数(如compare函数)写在类(如Solution类)里的话,作为sort()的参数时,要加上类的域(即Solution::compare),即使sort函数是在类内函数中进行的…
  • 原本想的简单了些,想用stack解决,wrong answer后悟出了第4步中所说的做法,然后用了set想要偷懒利用它的自动排序,但是忽略了一个问题就是set不能存相同的数…去网上查了查,发现了multiset。用multiset的时候还要注意,删除指定数字的时候,会删除该集合中所有的相同数字。所以应该用删除迭代器指针所指向的位置的方法。
  • 还有一些小问题,在代码的注释中写出来了…看代码就成

代码如下:

#include <vector>
#include <set>
#include <algorithm>
using namespace std;

// 自己构造的比较函数
bool compare(vector<int> a, vector<int> b) {
    return a[1] < b[1];
}

class Solution {
public:
    int scheduleCourse(vector<vector<int>>& courses) {
        sort(courses.begin(), courses.end(), compare);
        int usedTime = 0;
        // 原先两种行不通的方法
        //stack<vector<int>> takenCourses;
        //set<int> takenCourses;
        multiset<int> takenCourses;

        for (int i = 0; i < courses.size(); i++) {
            usedTime += courses[i][0];
            if (usedTime <= courses[i][1]) {
                takenCourses.insert(courses[i][0]);
            }
            else {

                if (courses[i][0] < *takenCourses.rbegin()) {
                    usedTime -= *takenCourses.rbegin();
                    // 这里不能用rbegin(), rbegin()和end()的类型并不一样
                    // 这里的erase好像只匹配end()或者cend()
                    takenCourses.erase(--takenCourses.end());
                    takenCourses.insert(courses[i][0]);
                }
                else {
                    usedTime -= courses[i][0];
                }
            }
        }
        return takenCourses.size();
    }
};

还需努力…加油吧

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值