随想录一刷Day35——贪心算法

文章目录

  • Day35_贪心算法
    • 13. 柠檬水找零
    • 14. 根据身高重建队列
    • 15. 用最少数量的箭引爆气球

Day35_贪心算法

13. 柠檬水找零

860. 柠檬水找零

思路:

一共只有三种支付金额,分别模拟找零情况,最后判断找零是否正常。

class Solution {
public:
    bool lemonadeChange(vector<int>& bills) {
        int len = bills.size();
        int sum5 = 0, sum10 = 0;
        for (int i = 0; i < len; ++i) {
            if (bills[i] == 5) { // 支付5元的情况
                sum5++;
            } else if (bills[i] == 10) { // 支付10元的情况
                sum10++;
                sum5--;
            } else if (bills[i] == 20) { // 支付20元的情况
                if (sum10 > 0) { // 找回一张10元一张5元
                    sum10--;
                    sum5--;
                } else { // 找回3张5元
                    sum5 -= 3;
                }
            }
            // 如果按照正常流程找完钱,发现有出现亏空的情况,则不能正常找零
            if (sum5 < 0 || sum10 < 0) return false;
        }
        return true;
    }
};

14. 根据身高重建队列

406. 根据身高重建队列
思路:

首先理解题意:重排是指,在不改变每个人的属性的情况下,通过重新排序,其属性能够满足新队列的要求。举例如下

  • 输入:[[7,0],[4,4],[7,1],[5,0],[6,1],[5,2]]
  • 输出:[[5,0],[7,0],[5,2],[6,1],[4,4],[7,1]]
    [5,0]交换位置之后还是可以满足前面没有比自己高的人这个属性

又是一个需要同时考虑两个因素的题,很难兼顾二者,于是回到分糖果的例子上,应该分别考虑,固定一个量,再考虑另一个量。那么优先考虑哪个呢?
如果按照 k i k_i ki来考虑,排完之后, k i k_i ki实际上是不对的,身高也不匹配,所以不能先按 k i k_i ki来排序。那就只能按照身高先排序,这样身高的分布显然时确定下来了,然后再考虑 k i k_i ki如果身高是升序排列,那么从身高高的开始按照 k i k_i ki将每个人放入新的队列中,后插入的比先插入的矮,不会影响先插入的人的 k i k_i ki,这样就可以完成重排了。

插入过程如下
排序完的people: [[7,0], [7,1], [6,1], [5,0], [5,2],[4,4]]
插入[7,0]:[[7,0]]
插入[7,1]:[[7,0],[7,1]]
插入[6,1]:[[7,0],[6,1],[7,1]]
插入[5,0]:[[5,0],[7,0],[6,1],[7,1]]
插入[5,2]:[[5,0],[7,0],[5,2],[6,1],[7,1]]
插入[4,4]:[[5,0],[7,0],[5,2],[6,1],[4,4],[7,1]]

温馨提示:建议这种动态的插入过程使用链表实现,如果是vector需要整体后移,底层实现存在一个成倍扩容,地址拷贝的操作,时间开销很大。详情参考卡哥说明

class Solution {
public:
    static bool cmp(const vector<int>& a, const vector<int> &b) {
        if (a[0] == b[0]) return a[1] < b[1]; // 身高相同时,按序号小的排前面
        return a[0] > b[0]; // 身高不同时,优先按身高降序排列
    }

    vector<vector<int>> reconstructQueue(vector<vector<int>>& people) {
        int people_size = people.size();
        sort(people.begin(), people.end(), cmp);
        list<vector<int>> que; // list底层是用链表实现的,插入效率比vector高
        for (int i = 0; i < people_size; ++i) {
            int offset = people[i][1];
            std::list<vector<int>>::iterator it = que.begin();
            while (offset--) {
                it++;
            }
            que.insert(it, people[i]);
        }
        return vector<vector<int>> (que.begin(), que.end());
    }
};

15. 用最少数量的箭引爆气球

452. 用最少数量的箭引爆气球
思路:

可以一枪打掉的气球要一次性打掉,这样可以保证使用了最少的数量。
那么要做的就是统计同一个区间内出现了多少个气球,即从某个气球开始,其后区间内的气球的最小右边位置与最大左边界位置之间的气球可以开一次枪全部打掉。实现过程如下

class Solution {
public:
    static bool cmp(const vector<int>& a, const vector<int>& b) {
        if (a[0] == b[0]) return a[1] < b[1];
        return a[0] < b[0];
    }

    int findMinArrowShots(vector<vector<int>>& points) {
        int points_num = points.size();
        int cnt = 0;
        sort(points.begin(), points.end(), cmp); // 按startx升序排序
        int i = 0, j = 1;
        while (i < points_num) {
            int min_right_x = points[i][1];
            while (j < points_num && min_right_x >= points[j][0]) { // 当前区间内最小的右边界之前气球都可以被同时打破
                if (points[j][1] < min_right_x) min_right_x = points[j][1]; // 更新区间的最小右边界
                j++;
            }
            cnt++; // 记录完一个区间开一枪
            i = j;
            j = i + 1;
        }
        return cnt;
    }
};

看了卡哥的题解,思路一致,但是代码更为简洁

class Solution {
public:
    static bool cmp(const vector<int>& a, const vector<int>& b) {
        if (a[0] == b[0]) return a[1] < b[1];
        return a[0] < b[0];
    }

    int findMinArrowShots(vector<vector<int>>& points) {
        int points_num = points.size();
        if (points_num == 0) return 0;
        sort(points.begin(), points.end(), cmp);
        int cnt = 1; // 至少使用一只箭
        for (int i = 1; i < points_num; ++i) {
            if (points[i][0] > points[i - 1][1]) { // 下一只箭的左区间大于当前最小的又区间了,则该气球不能和上一区间的气球同时打爆
                cnt++;
            } else { // 如果两气球有区间重叠
                points[i][1] = min(points[i][1], points[i - 1][1]); // 更新最小右边界,保证与下一气球判断区间时,右边界是当前重叠区间的最小值
            }
        }
        return cnt;
    }
};

你可能感兴趣的:(贪心算法,leetcode,算法)