代码随想录算法训练营第29天 | 134.加油站、135.分发糖果、860.柠檬水找零、406.根据身高重建队列
文章目录
134.加油站
解题思路
- 判断总油量和总消耗的大小关系,若总油量减去总消耗大于等于0,那么汽车可能行驶完一圈
- 从0出发,动态更新剩余油量,一旦当前站的剩余油量小于0,就将起点 start 设为当前站的下一站。最后返回最新的起点
局部最优:剩余油量的和一旦小于0,那么起始位置至少是当前站的下一站
全局最优:找到汽车可以行驶一圈的起始位置
代码实现
class Solution {
public:
int canCompleteCircuit(vector<int>& gas, vector<int>& cost) {
int curSum = 0;
int totalSum = 0;
int start = 0;
for (int i = 0; i < gas.size(); i++) {
curSum += gas[i] - cost[i];
totalSum += gas[i] - cost[i];
if (curSum < 0) {
start = i + 1;
curSum = 0;
}
}
if (totalSum < 0)
return -1;
return start;
}
};
题目总结
上面的解法可能不容易想到,思路和最大子序和那题有些相似。本题还可以使用暴力解法,遍历每一个加油站为起点的情况,模拟汽车行驶一圈的过程。
135.分发糖果
解题思路
确定一边孩子的评分之后,再确定另一边。先确定右边孩子的评分大于左边孩子的评分的情况
局部最优:只要右边孩子的评分比左边的高,那么右边孩子就会多得到一颗糖果
全局最优:相邻的孩子中,评分高的右边孩子获得比左边孩子更多的糖果
再确定左边孩子的评分大于右边孩子的评分的情况,从后往前遍历。
局部最优:右边孩子得到的糖果数加一后糖果数量和之前比较右边孩子评分大于左边孩子评分后得到的糖果数量间取最大值,保证第 i 个小孩得到的糖果数量既大于左边孩子得到的糖果数量也大于右边的
全局最优:相邻的孩子中,评分高的孩子获得更多的糖果。
代码实现
class Solution {
public:
int candy(vector<int>& ratings) {
vector<int> candyVec(ratings.size(), 1);
for (int i = 1; i < ratings.size(); i++) {
if (ratings[i] > ratings[i - 1]) {
candyVec[i] = candyVec[i - 1] + 1;
}
}
for (int i = ratings.size() - 2; i >= 0; i--) {
if (ratings[i] > ratings[i + 1]) {
candyVec[i] = max(candyVec[i], candyVec[i + 1] + 1);
}
}
int result = 0;
for (int i = 0; i < candyVec.size(); i++) {
result += candyVec[i];
}
return result;
}
};
题目总结
本题采用两次贪心的策略:
- 一次是从左到右遍历,只比较右边孩子的评分比左边大的情况
- 另一次是从右到左遍历,只比较左边孩子的评分比右边大的情况
860.柠檬水找零
解题思路
以下三种情况:
- 账单是5,直接收下
- 账单是10,消耗一个5,增加一个10
- 账单是20,优先消耗一个10和一个5,如果不够,那么再消耗3个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) {
if (five > 0 && ten > 0) {
five--;
ten--;
twenty++;
} else if (five >= 3) {
five -= 3;
twenty++;
} else {
return false;
}
}
}
return true;
}
};
题目总结
本题注意分析里面的逻辑,遇到没有思路的题目时,可以试着将所有可能出现的情况分析一下。
406.根据身高重建队列
解题思路
本题和分发糖果有点相似,在遇到两个维度权衡的时候,要先确定一个维度,再确定另一个维度。
先按照身高 h 来排序,身高一定是从大到小排(身高相同的话则 k 小的站前面),让高个子在前面。
按照身高排序之后,优先按身高高的 people 的 k 来插入,后序插入节点也不会影响前面已经插入的节点,最终按照 k 的规则完成了队列。
代码实现
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) {
sort(people.begin(), people.end(), cmp);
vector<vector<int>> que;
for (int i = 0; i < people.size(); i++) {
int position = people[i][1];
que.insert(que.begin() + position, people[i]);
}
return que;
}
};
题目总结
关于出现两个维度一起考虑的情况,其技巧都是确定一边然后贪心另一边,两边一起考虑,就会顾此失彼。