day-32 代码随想录算法训练营 贪心part02

122.买卖股票的最佳时机||

思路:计算每一次可得利润

55.跳跃游戏

思路一:覆盖范围内寻找可以到达终点的值

思路二:遍历数组,不断更新最大覆盖范围,从覆盖范围内寻找可以到达终点的值(不同的是需要加条件,当cover==i,也就是到了覆盖范围尽头时,如果nums[i]==0,就无法前进,直接break;

注意:思路一相当于思路二的简化版本,从思路二可以看出直接遍历覆盖范围可以达到明显优化。

class Solution {
public:
    bool canJump(vector<int>& nums) {
        int cover=0;
        if(nums.size()==1) return true;
        for(int i=0;i<nums.size()-1;i++){//遍历的是数组,所以需要终止条件
            cover=max(cover,nums[i]+i);
            if(cover>=nums.size()-1) return true;
            if(cover==i && nums[i]==0) break;//终止条件
        }
        return false;
    }
};

45.跳跃游戏||

思路一:
        从起点开始记录第一个最大覆盖范围lastcover,然后lastcover中不断更新第二个最大覆盖范围cover;
        当第一个最大覆盖范围走到尽头时,对尽头作出终点判断;
        未到达则把第一个最大覆盖范围lastcover更新成第二个最大覆盖范围cover
        此时再进行终点判断

上一次图解

class Solution {
public:
    int jump(vector<int>& nums) {
        int cover=0,precover=0;
        int res=0;
        for(int i=0;i<=precover;i++){
            cover=max(cover,nums[i]+i);
            if(i==precover){//上一个最大覆盖范围走到尽头
                if(precover==nums.size()-1) break;//判断是否走到终点
                else{
                    res++;//还需要跳跃
                    precover=cover;//更新最大覆盖范围
                    if(precover>=nums.size()-1) return res;//判断此时终点是否在最大覆盖范围内
                }
            }
        }
        return res;
    }
};

1005.K次取反后最大化的数组和

思路:先把取反后大的数先取反
形式一:记录正负数的个数,然后判断K和负数的大小
  • 当K<=负数个数,把所有负数取反为正
  • 当K>负数个数,
    • 首先判断是否存在0,存在0就可以对0操作,不改变数组和大小
    • 其次判断K与负数个数的差值的奇偶性,奇数时只需要改变最小的数值,偶数值不需要改变(因为可以在同一个数上多次取反)
class Solution {

public:
    int largestSumAfterKNegations(vector<int>& A, int K) {
        //排序之后,然后判断负数的个数是否小于等于k
        sort(A.begin(),A.end());
        int F=0,Z=0;
        for(int a:A){
            if(a>0) Z++;
            else if(a<0) F++;
        }
        //1.k<=F
        //2.K>F (1)K-F为奇数  (2)K-F为偶数
        int start=0,midF=F;
        if(K<=F){//K<=F时,只需要对负数取反
            while(K--)  A[start++]*=-1;
        }
        else if(K>F){//K>F时,先对负数取反,判断是否存在0,再判断奇偶
            while(midF--) A[start++]*=-1;
            if(A[0]!=0){
                if((K-F)%2!=0){
                    sort(A.begin(),A.end());
                    A[0]*=-1;
                }
            }
        }
        int res=0;
        for(auto it:A)  res+=it;//最后求数组和
        return res;
    }
};
形式二:优化版
class Solution {
static bool cmp(int a, int b) {
    return abs(a) > abs(b);
}
public:
    int largestSumAfterKNegations(vector<int>& A, int K) {
        sort(A.begin(), A.end(), cmp);       // 首先对绝对值进行排序(降序)
        for (int i = 0; i < A.size(); i++) { // 把负数全部取反
            if (A[i] < 0 && K > 0) {
                A[i] *= -1;
                K--;
            }
        }
        if (K % 2 == 1) A[A.size() - 1] *= -1; // 如果k还有,就直接修改最小值
        int result = 0;
        for (int a : A) result += a;        // 数组求和
        return result;
    }
};

134.加油站

思路一:遍历每一个加油站,然后记录当前剩余油量
剩余油量小于0时跳出当前开始
当遍历完成后,如果终止位置已经走了一圈,直接返回开始位置

结果:超出时间限制,注意数据量是10的5次方

思路二:贪心思想
首先记录当前油量,只要当前油量大于等于0,就可以继续遍历
当前油量小于0时,把出发位置放到当前遍历位置 i 的下一位(start=i+1),因为从 i 和 i 之前的位置出发一定无法走完全程;(注意,此时需要更新剩余油量为0,因为已经改变了出发位置为下一位,剩余油量是从0计算的)
遍历结束之后,只要剩余油量大于等于0,说明记录的start是可以走完全程的(因为存在唯一解)
class Solution {
public:
    int canCompleteCircuit(vector<int>& gas, vector<int>& cost) {
        int n=cost.size();
        int totalGas=0;
        int curGas=0;
        int start=0;
        for(int i=0;i<n;i++){
            totalGas+=gas[i]-cost[i];//计算总剩余油量
            curGas+=gas[i]-cost[i];//计算从start开始的剩余油量
            if(curGas<0){//从start走到i无法继续走了,说明0-i都无法走通
                start=i+1;//从下一个位置开始
                curGas=0;//重新开始为0
            }
                
        }
        if(totalGas>=0) return start;//说明总油量够走完全程
        return -1;
    }
};

135.分发糖果

思路:考虑两边孩子和当前位置孩子的评分对比情况
class Solution {
public:
    int candy(vector<int>& ratings) {
        int n=ratings.size();
        vector<int>candys(n,1);
        for(int i=1;i<n;i++){//考虑右边孩子比左边孩子评分高
            if(ratings[i]>ratings[i-1])
                candys[i]=candys[i-1]+1;
        }
        for(int i=n-2;i>=0;i--){//考虑左边孩子比右边孩子评分高
            if(ratings[i]>ratings[i+1])
                candys[i]=max(candys[i],candys[i+1]+1);
        }
        int res=0;
        for(auto it:candys)//总和
            res+=it;
        return res;
    }
};

860.柠檬水找零

分析:已知顾客的金额已经固定,所以找的零钱也固定
思路一:面向题目编程
class Solution {
public:
    bool lemonadeChange(vector<int>& bills) {
        unordered_map<int,int>bils;
        int n=bills.size();
        for(int i=0;i<n;i++){
            cout<<i<<endl;
            if(bills[i]==5)//5元直接收
                bils[5]++;
            else{
                if(bills[i]==20){//找15元
                    if(bils[10]>0 && bils[5]>0){//直接找10 和 5
                        bils[20]++;
                        bils[10]--;
                        bils[5]--;
                    }else if(bils[5]>=3){//找3 张5
                        bils[5]-=3;
                        bils[20]++;
                    }
                    else return false;
                }else{//找5 元
                    if(bils[5]>0){
                        bils[10]++;
                        bils[5]--;
                    }
                    else return false;
                }
            }
        }
        return true;
    }
};

406.根据身高重建队列

思路一:考虑多个维度,数组插入,自定义比较函数
维度一:身高
维度二:前面多少个比自己高或和自己一样高
在根据维度一排序后,然后根据维度二进行插入排序
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;
    }
};

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值