代码随想录算法训练营day33|第八章 贪心算法 part03

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

本题简单一些,估计大家不用想着贪心 ,用自己直觉也会有思路。 

https://programmercarl.com/1005.K%E6%AC%A1%E5%8F%96%E5%8F%8D%E5%90%8E%E6%9C%80%E5%A4%A7%E5%8C%96%E7%9A%84%E6%95%B0%E7%BB%84%E5%92%8C.html

文章里面是直接让数组按照绝对值从大到小来排列,确实,这种方法可以在正向遍历改变符号的时候优先改变绝对值大的负数,而在k是奇数的时候改变绝对值最小的符号,这种贪心的策略是贪绝对值大小,只有绝对值较大者才能对数组总和有显著影响。

值得注意的是,在使用sort函数的时候需要自己写比较函数作为第三个参数,注意比较函数的第一个参数是排列的前者第二个参数是后者,要实现绝对值从大到小排列要求二者绝对值函数的值要前者大于后者。

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; // 第三步
        int result = 0;
        for (int a : A) result += a;        // 第四步
        return result;
    }
};

134. 加油站 

本题有点难度,不太好想,推荐大家熟悉一下方法二 

https://programmercarl.com/0134.%E5%8A%A0%E6%B2%B9%E7%AB%99.html

我感觉这道题确实比较难想到,主要是对贪心算法的步骤不太熟练。首先就是将一个问题分成几个小问题,这个题目是将走完整个数组的大问题分成能走到当前遍历到的下标处的小问题,具体也就是如果开始出发的下标处不能走到当前的下标处(也就是走到这汽油变成负数了),那就说明从当前下标以及之前的下标开始,都不能到达当前的下一个下标处,而如果一定存在一个开始处下标能走完整个数组(也就是加的汽油的总和不小于消耗的汽油的总和),那就一定是之后的下标作为开始处才可以实现。

因为既然能走到当前下标就意味着不往下走剩余的汽油一定是不小于0的,而又确实走不到下一个下标处,证明当前这个点增加的汽油一定小于走到下一个点消耗的汽油,并且再消耗了剩余积累的汽油还走不到,那么从当前点作为开始就走不通;那么至于为什么从原定开始处一直到当前点中间的哪些点也不能走得通,因为其实从原定开始点到当前点都是一定汽油剩余量不小于0的,那么中间的任意一个点都一定吃了之前的剩余汽油的红利,就是吃了红利还是没能走到下一个点,那么不吃红利就一定走不到下一个点。

值得一提的是,这道题不能只通过判断当前剩余扽汽油总量来判断选定的起始点是否正确,还要判断是否真的能找到一个起始点能走到原点,否则就少考虑了只有最后一个点是正数的情况,只有当证明了有且只有一个开始位置能走遍全程才能通过贪心算法方法确定。

int canCompleteCircuit(vector<int>& gas, vector<int>& cost) {
        int begin=0;
        int total=0;
        int cur=0;
        for(int i=0;i<gas.size();i++){
            cur+=gas[i]-cost[i];
            total+=gas[i]-cost[i];
            if(cur<0){
                cur=0;
                begin=i+1;
            }
        }
        if(total<0) return -1;
        return begin;
    }

135. 分发糖果 

本题涉及到一个思想,就是想处理好一边再处理另一边,不要两边想着一起兼顾,后面还会有题目用到这个思路 

https://programmercarl.com/0135.%E5%88%86%E5%8F%91%E7%B3%96%E6%9E%9C.html

首先要考虑到对于一个位置的糖果数量需要考虑到它两边的糖果数量和等级差异(例如当前等级比右边的等级高,那得到的糖果数量就必须至少是右边糖果+1,再考虑到左边等级可能会增加),然而是没有办法同时考虑两边的情况的,因为一次遍历只能沿着一个方向,注定没法同时得到它两边准确的糖果数量,所以需要两次遍历,一次针对右边大于左边的情况,一次针对左边大于右边的情况。

以这个顺序为例,第一次遍历(右边大于左边的情况)必须是正向遍历,因为毕竟要用到左边的糖果数量,所以数组必须从左至右依次修正数组,一旦右边大于左边,那右边就等于左边糖果+1;
第二次遍历(左边大于右边的情况)必须是反向遍历,因为也会用到右边的糖果数量(因为是最终的遍历,所以右边的糖果数量必须是是准确值),这样只能反向修正数组,因为要利用到上一次遍历的结果,所以一旦左边大于右边,需要在当前糖果数量和右边糖果数量+1中选择较大者,只有取最大值才能让两次便利的结果都有用。

事实上,这两次遍历的顺序可以随意颠倒,也就是先考虑哪一种情况都可以,但是后一次遍历必须要用到前一次遍历的结果(也就是取较大值),这样才没有白遍历。

int candy(vector<int>& ratings) {
        vector<int> candys(ratings.size(),1);
        //对于右边大于左边
        for(int i=1;i<ratings.size();i++){
            if(ratings[i]>ratings[i-1]) candys[i]=candys[i-1]+1;
        }
        //对于左边大于右边
        for(int i=ratings.size()-2;i>=0;i--){
            if(ratings[i]>ratings[i+1]) candys[i]=max(candys[i],candys[i+1]+1);
        }
        int sum=0;
        for(int a:candys) sum+=a;
        return sum;
    }
  • 24
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
代码随想录算法训练营是一个优质的学习和讨论平台,提供了丰富的算法训练内容和讨论交流机会。在训练营中,学员们可以通过观看视频讲解来学习算法知识,并根据讲解内容进行刷题练习。此外,训练营还提供了刷题建议,例如先看视频、了解自己所使用的编程语言、使用日志等方法来提高刷题效果和语言掌握程度。 训练营中的讨论内容非常丰富,涵盖了各种算法知识点和解题方法。例如,在第14天的训练营中,讲解了二叉树的理论基础、递归遍历、迭代遍历和统一遍历的内容。此外,在讨论中还分享了相关的博客文章和配图,帮助学员更好地理解和掌握二叉树的遍历方法。 训练营还提供了每日的讨论知识点,例如在第15天的讨论中,介绍了层序遍历的方法和使用队列来模拟一层一层遍历的效果。在第16天的讨论中,重点讨论了如何进行调试(debug)的方法,认为掌握调试技巧可以帮助学员更好地解决问题和写出正确的算法代码。 总之,代码随想录算法训练营是一个提供优质学习和讨论环境的平台,可以帮助学员系统地学习算法知识,并提供了丰富的讨论内容和刷题建议来提高算法编程能力。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *2* *3* [代码随想录算法训练营每日精华](https://blog.csdn.net/weixin_38556197/article/details/128462133)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 100%"] [ .reference_list ]

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值