题目链接:860.柠檬水找零
文章讲解:代码随想录 860.柠檬水找零讲解
视频讲解:贪心算法,看上去复杂,其实逻辑都是固定的!LeetCode:860.柠檬水找零
思路和解法
题目:
在柠檬水摊上,每一杯柠檬水的售价为 5 美元。顾客排队购买你的产品,(按账单 bills 支付的顺序)一次购买一杯。
每位顾客只买一杯柠檬水,然后向你付 5 美元、10 美元或 20 美元。你必须给每个顾客正确找零,也就是说净交易是每位顾客向你支付 5 美元。
注意,一开始你手头没有任何零钱。
给你一个整数数组 bills ,其中 bills[i] 是第 i 位顾客付的账。如果你能给每位顾客正确找零,返回 true ,否则返回 false 。
想法:
关键点在于处理面值20的时候优先消耗面值10的。
class Solution {
public:
bool lemonadeChange(vector<int>& bills) {
//维护三种面值 每次收钱也是三种情况 分情况判断即可
int five = 0, ten = 0, twenty = 0;
for (int i = 0; i < bills.size(); i++) {
if (bills[i] == 5) five++;
if (bills[i] == 10) {
if (five > 0) {
five--;
ten++;
} else {
return false;
}
}
if (bills[i] == 20) {
//优先消耗10面值,5面值更万能
if (ten > 0 && five > 0) {
ten--;
five--;
twenty++;
} else if (five >= 3) {
five -= 3;
twenty++;
} else {
return false;
}
}
}
return true;
}
};
题目链接:406.根据身高重建队列
文章讲解:代码随想录 406.根据身高重建队列讲解
视频讲解:贪心算法,不要两边一起贪,会顾此失彼 | LeetCode:406.根据身高重建队列
思路和解法
题目:
假设有打乱顺序的一群人站成一个队列,数组 people 表示队列中一些人的属性(不一定按顺序)。每个 people[i] = [hi, ki] 表示第 i 个人的身高为 hi ,前面 正好 有 ki 个身高大于或等于 hi 的人。
请你重新构造并返回输入数组 people 所表示的队列。返回的队列应该格式化为数组 queue ,其中 queue[j] = [hj, kj] 是队列中第 j 个人的属性(queue[0] 是排在队列前面的人)。
想法:
链表实现效率更高,但是链表插入那里不太好写。原因是对迭代器类型不熟悉。
class Solution {
public:
//自定义排序规则
static bool cmp(vector<int>& a, 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) {
//先按照h从大到小排序,如果相等就按照k从小到大排序
sort(people.begin(), people.end(), cmp);
//开始插入,为了提高插入效率 使用list 底层为链表实现
list<vector<int>> que;
for (int i = 0; i < people.size(); i++) {
//先计算插入位置
int position = people[i][1];
//这样写不对 可能list的迭代器不是随机访问迭代器?
// list<vector<int>>::iterator it = que.begin() + position;
list<vector<int>>::iterator it = que.begin();
while (position--) {
it++;
}
que.insert(it, people[i]);
}
vector<vector<int>> result(que.begin(), que.end());
return result;
}
};
题目链接:452. 用最少数量的箭引爆气球
文章讲解:代码随想录 452. 用最少数量的箭引爆气球讲解
视频讲解:贪心算法,判断重叠区间问题 | LeetCode:452.用最少数量的箭引爆气球
思路和解法
题目:
有一些球形气球贴在一堵用 XY 平面表示的墙面上。墙面上的气球记录在整数数组 points ,其中points[i] = [xstart, xend] 表示水平直径在 xstart 和 xend之间的气球。你不知道气球的确切 y 坐标。
一支弓箭可以沿着 x 轴从不同点 完全垂直 地射出。在坐标 x 处射出一支箭,若有一个气球的直径的开始和结束坐标为 xstart,xend, 且满足 xstart ≤ x ≤ xend,则该气球会被 引爆 。可以射出的弓箭的数量 没有限制 。 弓箭一旦被射出之后,可以无限地前进。
给你一个数组 points ,返回引爆所有气球所必须射出的 最小 弓箭数 。
想法:
不额外用变量记录前面所有气球的最前面的结束位置,而是每次都更新当前气球的结束位置,这个很难想到。
class Solution {
public:
//自定义比较规则
static bool cmp(vector<int>& a, vector<int>& b) {
return a[0] < b[0];
}
int findMinArrowShots(vector<vector<int>>& points) {
//先判断特殊情况 其实这里题目中写了气球数大于等于1
if (points.size() == 0) return 0;
//先按照起始位置从小到大排序
sort(points.begin(), points.end(), cmp);
//开始遍历处理
//先初始化一个箭数 至少为一
int result = 1;
for (int i = 1; i < points.size(); i++) {
//什么情况下,才必须要另一支箭呢?当前气球的起始位置大于前面所有气球中最前面的结束位置
//那么我就需要记录这个最前面的结束位置,讲解中并没有单独用一个变量记录,而是修改当前的气球范围
//也就是说每次遍历时,如果不需要下一支箭,那么就假设当前的气球结束位置就是前面所有气球中最前面的结束位置
//需要另一支箭时就不用假设了,后面的假设也假设为这个气球的结束位置,所以每次更新假设都是把前一个气球的结束位置赋值过来
if (points[i][0] > points[i - 1][1]) {
result++;
} else {
//这里写错了,没考虑到当前气球半径非常小,导致当前气球结束位置小于前面所有气球的结束位置的情况
// 错误写法:points[i][1] = points[i - 1][1];
points[i][1] = min(points[i - 1][1], points[i][1]);
}
}
return result;
}
};
一点感悟
说实话,贪心太难总结了,我甚至自己理解了做法都没有办法做总结,只能是多看多练了,目前唯一能做的就是多练几遍,保证完全理解,希望以后再看到类似题目能有印象和经验。