代码随想录–贪心算法
day 29 贪心算法第三天
一、力扣134–加油站
代码随想录题目链接:代码随想录
在一条环路上有 n 个加油站,其中第 i 个加油站有汽油 gas[i] 升。
你有一辆油箱容量无限的的汽车,从第 i 个加油站开往第 i+1 个加油站需要消耗汽油 cost[i] 升。你从其中的一个加油站出发,开始时油箱为空。
给定两个整数数组 gas 和 cost ,如果你可以按顺序绕环路行驶一周,则返回出发时加油站的编号,否则返回 -1 。如果存在解,则 保证 它是 唯一 的。
用贪心算法分析的话,就是每一步检查补充减消耗,并且记录当前的油箱剩余量
如果剩余量为负数,说明这之前的加油站都不能是起点,包括当前的这个,所以就可以更新起点
另外还需要一个变量记录全程的补充与消耗,如果这个变量小于0则说明不管怎么样都走不完一圈
代码如下:
class Solution {
public:
int canCompleteCircuit(vector<int>& gas, vector<int>& cost) {
int currSum = 0;
int totalSum = 0;
int start = 0;
for(int i = 0; i < gas.size(); i ++)
{
currSum += gas[i] - cost[i];
totalSum += gas[i] - cost[i];
if(currSum < 0)
{
start = i + 1;
currSum = 0;
}
}
if(totalSum < 0) return -1;
else return start;
}
};
二、力扣135–分发糖果
代码随想录题目链接:代码随想录
n 个孩子站成一排。给你一个整数数组 ratings 表示每个孩子的评分。
你需要按照以下要求,给这些孩子分发糖果:
每个孩子至少分配到 1 个糖果。
相邻两个孩子评分更高的孩子会获得更多的糖果。
请你给每个孩子分发糖果,计算并返回需要准备的 最少糖果数目 。
目前为止刷到的第二个困难题目
直接的想法是左右各遍历一遍,向左遍历时如果左孩子更大,就给糖果总数加一,右遍历同理
但是这样会漏掉一些情况
分发糖果的数量并不应该单纯用sum记录,而是应该用vector记录
这样如果要增加糖果,就不用++,而是直接让该孩子的糖果数等于旁边孩子糖果数+1
否则有可能会导致孩子糖果数++后和旁边一样多,但是它成绩又高于另外两个
代码如下:
class Solution {
public:
int candy(vector<int>& ratings) {
vector<int> candyVec(ratings.size(), 1);
int sum = 0;
int base = ratings.size();
for(int i = 1; i < base ; i ++)
{
if(ratings[i] > ratings[i - 1]) candyVec[i] = candyVec[i - 1] + 1;
}
for(int i = base - 2; i >= 0; i --)
{
if(ratings[i] > ratings[i + 1]) candyVec[i] = max(candyVec[i], candyVec[i + 1] + 1);
}
for(int ele:candyVec) sum += ele;
return sum;
}
};
三、力扣860–柠檬水找零
代码随想录题目链接:代码随想录
在柠檬水摊上,每一杯柠檬水的售价为 5 美元。顾客排队购买你的产品,(按账单 bills 支付的顺序)一次购买一杯。
每位顾客只买一杯柠檬水,然后向你付 5 美元、10 美元或 20 美元。你必须给每个顾客正确找零,也就是说净交易是每位顾客向你支付 5 美元。
注意,一开始你手头没有任何零钱。
给你一个整数数组 bills ,其中 bills[i] 是第 i 位顾客付的账。如果你能给每位顾客正确找零,返回 true ,否则返回 false 。
模拟找零游戏,用一个vector去存5、10、20元的钞票数量
那么做过小生意的都知道找零钱尽量给大钞,因为小面额的更万能
思路也就是这样了,代码如下:
class Solution {
public:
bool lemonadeChange(vector<int>& bills) {
int charges[3] = {0};
for(int bill : bills)
{
if(bill == 5) charges[0] ++;
else if(bill == 10)
{
if(charges[0] > 0)
{
charges[0] --;
charges[1] ++;
}
else return false;
}
else
{
if(charges[0] > 0 && charges[1] > 0)
{
charges[0] --;
charges[1] --;
charges[2] ++;
}
else if(charges[0] >=3)
{
charges[0] -= 3;
charges[2] ++;
}
else return false;
}
}
return true;
}
};
实际上charges可以只有两位,因为20是不可能用于找零的,不过模拟游戏就模拟整套吧
四、力扣406–根据身高重建队列
代码随想录题目链接:代码随想录
假设有打乱顺序的一群人站成一个队列,数组 people 表示队列中一些人的属性(不一定按顺序)。每个 people[i] = [hi, ki] 表示第 i 个人的身高为 hi ,前面 正好 有 ki 个身高大于或等于 hi 的人。
请你重新构造并返回输入数组 people 所表示的队列。返回的队列应该格式化为数组 queue ,其中 queue[j] = [hj, kj] 是队列中第 j 个人的属性(queue[0] 是排在队列前面的人)。
那么就是对这个队列排序,个子高的在前面,个子一样高的看k,k小的在前面
然后再按照k的大小对这个队列进行排序,让k能够正确反应队伍的信息
排序部分就自定义比较函数然后调用sort即可
bool cmp(vector<int>a, vector<int>b)
{
if(a[0] == b[0]) return a[1] < b[1];
else return a[0] > b[0];
}
这时候再根据k插入,而这里也展现出从高到低排序的重要性
因为这样先插入的就是个子高的,并不影响个子矮的插入,否则还要加判断,这也是贪心算法的体现
代码如下:
class Solution {
public:
static bool cmp(vector<int> a, vector<int> b)
{
if(a[0] == b[0]) return a[1] < b[1];
else 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 ++)
{
que.insert(que.begin()+people[i][1],people[i]);
}
return que;
}
};