860.柠檬水找零
贪心策略:使手上尽可能留有更多的面值为5的钞票,即找零优先想办法用面值为10的钞票找零
用变量记录手上钞票数量,然后遍历bills模拟找零即可
找零会出现三种情况
- 收到面值5的钞票,就可以直接收下
- 收到面值10的钞票,需要用5找零
- 收到面值20的钞票,优先用一张10和一张5找零,其次选择三张5找零(贪心的体现)
class Solution {
public:
bool lemonadeChange(vector<int>& bills) {
int five = 0, ten = 0, twenty = 0;
for (int bill : bills) {
// 情况一
if (bill == 5) five++;
// 情况二
if (bill == 10) {
if (five <= 0) return false;
ten++;
five--;
}
// 情况三
if (bill == 20) {
// 优先消耗10美元,因为5美元的找零用处更大,能多留着就多留着
if (five > 0 && ten > 0) {
five--;
ten--;
twenty++; // 其实这行代码可以删了,因为记录20已经没有意义了,不会用20来找零
} else if (five >= 3) {
five -= 3;
twenty++; // 同理,这行代码也可以删了
} else return false;
}
}
return true;
}
};
406.根据身高重建队列
思路如下
首先按照身高从大到小排序(根据题意,身高相同的话则k小的站前面)
按照身高排序之后,优先按身高高的people的k来插入,后序插入节点也不会影响前面已经插入的节点,最终按照k的规则完成了队列
这种分两步进行操作的思路在135. 分发糖果也有所涉及
class Solution {
public:
static bool cmp(const vector<int>& a, const vector<int>& b) {
if (a[0] == b[0]) return a[1] < b[1]; // 当身高相同,则按k由小到大排
return a[0] > b[0]; // 按身高由大到小排
}
vector<vector<int>> reconstructQueue(vector<vector<int>>& people) {
sort (people.begin(), people.end(), cmp);
vector<vector<int>> que;
for (int i = 0; i < people.size(); i++) {
int position = people[i][1]; // 插入位置就是k
que.insert(que.begin() + position, people[i]); // 在指定位置之前插入
}
return que;
}
};
452.用最少数量的箭引爆气球
本题首先要读懂题目,这是一道重叠区间的问题
本题的基本思路是:先按照左边界排序(由小到大),然后从左向右遍历,遍历过程中看看是否需要增加弓箭
sort(points.begin(), points.end(), [](vector<int> & l, vector<int> & r){ return l[0] < r[0]; });
以上是排序后的情况,接下来我们就从左往右遍历来看是否需要增加弓箭
注意,我们看的是“是否需要增加弓箭”,我们设置弓箭初始值为1,表明至少需要一支弓箭,然后我们从索引1,即气球2开始遍历。因为初始值已经为1了,遍历气球1不可能将弓箭增加到2,只有可能遍历到气球2才可能增加弓箭(注意这里的逻辑)
怎么判断是否需要增加弓箭?
几种情况如下
第一种情况,遍历到 i,i的左边界大于 i-1 的右边界,此时一定需要增加一支弓箭
第二种情况,遍历到i,i的左边界小于等于 i-1 的右边界,表明 i 和 i-1 是重叠气球,此时不需要增加弓箭,一支箭就能够将这两个气球引爆
在这种情况下,我们一定会继续遍历到 i+1,此时,如果 i+1 的左边界小于等于重叠气球中右边边界的最小值(如下左),说明 i-1、i、i+1 重叠,则还能够继续往后遍历而不需要增加弓箭,如果i+1 的左边界大于重叠气球中右边边界的最小值了(如下右),则需要增加弓箭
在我们代码实现中,我们遍历到 i 时,如果发生重叠,可以维护其右边界就是已重叠气球中右边边界的最小值,这样能够以一种简洁的形式实现本题
整体代码如下
class Solution {
public:
int findMinArrowShots(vector<vector<int>>& points) {
if (points.size() == 0) return 0;
if (points.size() == 1) return 1; // 两种可以直接判断的情况,0个气球或1个气球
// 首先对气球按照左边界排序
sort(points.begin(), points.end(), [](vector<int> & l, vector<int> & r){ return l[0] < r[0];});
int result = 1; // 气球数量大于1,至少需要一支弓箭,我们遍历过程中看的是“是否需要增加弓箭”
for (int i = 1; i < points.size(); ++i) {
if (points[i][0] > points[i-1][1]) result++; // 增加弓箭的逻辑
else { // 出现重叠
points[i][1] = min(points[i][1], points[i-1][1]); // 维护右边界就是重叠气球中右边边界的最小值
}
}
return result;
}
};
回顾总结
重叠区间这道题的过程好好品味一下
贪心算法需要好好读题