860.柠檬水找零
这题的数值设定地很巧,导致解法也很简单,重点在于对每类钞票进行分解:
20元:10元钞票只能被20元找零消耗,所以遇到20元钞票优先消耗10+5,如果没有10元钞票则消耗三张5元钞票
10元:只能消耗一张5元钞票
5元:不需要任何消耗
bool lemonadeChange(vector<int>& bills) {
vector<int> money(2, 0); // 存放5和10元钞票的张树
for (int i = 0; i < bills.size(); ++i) {
if (bills[i] == 5)
++money[0];
else if (bills[i] == 10) {
++money[1];
--money[0];
}
else {
if (money[1] > 0) {
--money[0];
--money[1];
}
else
money[0] -= 3;
}
if (money[0] < 0)
return false;
}
return true;
}
406.根据身高重建队列
难啊,凭自己的理解对k进行优先排序,写了半天没写出来。实际上应该对身高h进行优先排序。
排序规则:优先对身高进行降序排列,相同身高则k小的排前面。该排序规则能实现:
· 对于一个位置,当前位置之前所有人的身高都大于当前位置人的身高。所以直接将这个人插入到其对应索引k的位置即可。
· 按以上规则插入,如果之后还有人插入到这个人的前面,由于插入者的身高必定比这个人小,所以并不会影响这个人k的正确性(相同身高下k值小的优先插入,所以相同身高不会插入到前面)
class cmp {
public:
bool operator()(const vector<int>& v1, const vector<int>& v2) {
if (v1[0] != v2[0]) {
// 身高高的排前面
return v1[0] > v2[0];
}
else {
// 身高相同时k小的排前面
return v1[1] < v2[1];
}
}
};
vector<vector<int>> reconstructQueue(vector<vector<int>>& people) {
// 先排个序
std::sort(people.begin(), people.end(), cmp());
// 直接逐个插入到索引k的位置
vector<vector<int>> queue;
for (int i = 0; i < people.size(); ++i) {
queue.insert(queue.begin() + people[i][1], people[i]);
}
return queue;
}
对于有多个维度的问题,应该分开考虑每一个维度,最后再对各个维度的结果进行合并或取舍
452.用最少数量的箭引爆气球
这题也应用了排序,重点是理解排序的作用,以及排序后使用右边界来判断是否重叠
排序的目的:使可能发生重叠的气球尽可能聚在一起
排序规则:左边界从小到大排序,左边界相同的情况下右边界从大到小排序(右边界排序规则不重要,重点在于左边界的排序)
排序能实现的效果:
如果未排序,对于判断是否重叠需要四次判断:
· 判断左边界是否大于上一个气球的左边界,且小于上一个气球的右边界(2次)
· 判断右边界是否大于上一个气球的左边界,且小于上一个气球的右边界(2次)
· 就算进行的更多的判断,由于可能重叠的气球没有聚在一起,还是可能会浪费箭数
排序后,判断是否重叠仅需要一次判断:只需要判断当前气球的左边界是否小于上一只箭的最大右边界。
· 由于左边界经过排序,如果当前气球的左边界不在上一个气球区间内,则右边界一定也不在其区间内,所以只需要判断左边界即可
· 由于左边界经过排序,所以当前左边界必定小于等于上一个气球的左边界,所以只需要判断与上一个气球的右边界相比是否符合条件即可
· 对于用于对比的右边界,不应该直接对比上一个气球的右边界,而应该对比上一只箭的最大右边界(即该只箭所射爆的所有气球中右边界的最小值),超过该值说明需要一只新箭
class cmp {
public:
// 使用常量引用能大幅缩短运行时间
bool operator()(const vector<int>& v1, const vector<int>& v2) {
if (v1[0] != v2[0]) {
// 左边界小的排在前面
return v1[0] < v2[0];
}
else {
// 如果左边界相同,则右边界小的排在后面
return v1[1] > v2[1];
}
}
};
int findMinArrowShots(vector<vector<int>>& points) {
// 先排序使重叠的气球尽量聚在一起
std::sort(points.begin(), points.end(), cmp());
int ans = 1;
// 由于已经对左边界进行了排序,所以只需要判断右边界是否符合条件即可
// 即下一个气球的左边界不能超过当前这只箭所能达到的最大右边界
int right = points[0][1];
for (int i = 1; i < points.size(); ++i) {
// 超出当前这只箭所能达到的最大右边界时,箭数+1,同时更新新的这只箭的右边界
if (points[i][0] > right) {
++ans;
// 更新新箭的右边界
right = points[i][1];
}
else {
// 更新当前这只箭的右边界
right = std::min(right, points[i][1]);
}
}
return ans;
}