代码随想录算法训练营 || 贪心算法 1005 134 135

Day29

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

力扣题目链接

  • 给定一个整数数组 A,我们只能用以下方法修改该数组:我们选择某个索引 i 并将 A[i] 替换为 -A[i],然后总共重复这个过程 K 次。(我们可以多次选择同一个索引 i。)

  • 以这种方式修改数组后,返回数组可能的最大和。

  • 输入:A = [2,-3,-1,5,-4], K = 2

  • 输出:13

  • 解释:选择索引 (1, 4) ,然后 A 变为 [2,3,-1,5,4]。

思路

  • 我们可以先把数组按照绝对值从大到小进行排序

  • [5,-4,-3,2,-1],对这个数组,从前往后进行遍历,遇到负数就变为相反数,并把k--;循环结束条件是遍历到数组末尾或k=0

  • 如果k=0跳出循环,那就可以直接返回改变后数组的和

  • 如果数组遍历结束跳出循环,这时k还大于零

  • 如果k是偶数,那其实不用管了,取反两次还是自己

  • 如果k是奇数,就把数组最后一位的元素取反,因为它的绝对值最小,取反后让sum变小的最少

代码

class Solution {
    public int largestSumAfterKNegations(int[] nums, int k) {
        nums = IntStream.of(nums)
                .boxed()
                .sorted((o1, o2) -> Math.abs(o2) - Math.abs(o1))
                .mapToInt(Integer::intValue).toArray();//把数组按绝对值大小进行排序
        for (int i = 0; i < nums.length && k > 0; i++) {//循环结束条件是遍历结束或k为0了
            if (nums[i] < 0) {
                nums[i] = -nums[i];//把绝对值最大的负数取反
                k--;//处理k
            }
        }
        if (k % 2 == 1) nums[nums.length - 1] = -nums[nums.length - 1];//k为偶数不用管,为奇数就把绝对值最小的正数取反
        return Arrays.stream(nums).sum();
    }
}

134. 加油站

力扣题目链接

在一条环路上有 N 个加油站,其中第 i 个加油站有汽油 gas[i] 升。

你有一辆油箱容量无限的的汽车,从第 i 个加油站开往第 i+1 个加油站需要消耗汽油 cost[i] 升。你从其中的一个加油站出发,开始时油箱为空。

如果你可以绕环路行驶一周,则返回出发时加油站的编号,否则返回 -1。

思路

  • 暴力遍历

  • 计算gas和cost数组的差数组,对这个arr进行遍历

  • 如果arr[i]小于0,那直接continue,这个加油站的油跑不到下一个加油站(能跑到上一个加油站么,不需要考虑,因为可以看上一个加油站能不能跑到这个加油站)

  • 如果大于0,那就从这里开始循环,跑一圈(取余操作),如果跑的过程中haveGas小于零,那从这个加油站开始就跑不了一圈,结束内层循环;如果发现能跑一圈,那直接返回i

  • 最后外层循环结束,每个加油站都看完了还没有找到能跑一圈的,返回-1;

  • 比较巧妙的解法

  • 还是先给出rest数组

  • 对rest数组进行遍历,计算数组前i个元素的和,并计算部分和的最小值

  • 如果遍历结束,数组所有元素和加起来小于0,说明肯定跑不到,返回-1

  • 如果部分和的最小值都大于零,那随便跑,返回0

  • 剩下的情况就是要研究从哪个加油站开始跑的,我们这时倒着开始遍历,找恰好能填平最小部分和的元素位置

  • 贪心算法

  • 局部最优:当前累加rest[i]的和curSum一旦小于0,起始位置至少要是i+1,因为从i之前开始一定不行。全局最优:找到可以跑一圈的起始位置

  • 首先需要知道,rest数组累加如果不小于0,那就一定能跑玩

  • 对rest数组进行遍历,计算部分和,如果发现计算到i,部分和为负数,那其实从0到i这一段,不可能能作为起点,那就从i+1开始继续计算部分和,部分和从0开始重新计算

  • 如果遍历结束,累加小于0,跑不完返回-1

  • 否则一定能跑完,其实就是部分和大于零的初始值,用index进行记录

代码

class Solution {
    public int canCompleteCircuit(int[] gas, int[] cost) {
        int[] arr = new int[cost.length];
        for (int i = 0; i < arr.length; i++) {
            arr[i] = gas[i] - cost[i];//计算arr数组
        }
        for (int i = 0; i < cost.length; i++){
            if (arr[i] < 0) continue;//小于0,跑不到下一个加油站,直接continue
            int index = i;//记录i
            int haveGas = arr[i];//记录这个加油站的油量
            while (haveGas >= 0){//haveGas小于零了,说明跑不够一圈,进行下一次外层循环
                index = (index + 1) % arr.length;//注意是循环,最后一位的下一位是第一位
                haveGas += arr[index];//不断更新油量,可能多也可能少
                if (index == i) return i;//如果跑了一圈了,直接返回i
            }
        }
        return -1;//外层循环结束了都没有返回,返回-1
    }
}

class Solution {
    public int canCompleteCircuit(int[] gas, int[] cost) {
        int[] rest = new int[gas.length];
        int sum = 0;
        int min = 0;
        for (int i = 0; i < rest.length; i++) {
            rest[i] = gas[i] - cost[i];
            sum += rest[i];//计算部分和
            min = Math.min(min,sum);//计算最小部分和
        }
        if (sum < 0) return -1;//油量不够耗油量,肯定跑不完,这里其实是剪枝操作,这一步也可以省去,填不平就返回-1
        if (min == 0) return 0;//最小部分和是0.没更新过,那随便跑
        for (int i = rest.length - 1; i >= 0;i--){//倒着遍历
            min += rest[i];//不断加上油量
            if (min >= 0) return i;//恰好填平,返回i
        }
        return -1;//其实不会执行到这里,但还是要返回,因为只要sum >= 0,就一定有办法让跑完
    }
}

class Solution {
    public int canCompleteCircuit(int[] gas, int[] cost) {
        int curSum = 0;
        int totalSum = 0;
        int index = 0;
        for (int i = 0; i < gas.length; i++) {
            curSum = curSum + gas[i] - cost[i];
            totalSum = totalSum + gas[i] - cost[i];
            if (curSum < 0){//部分和小于零
                curSum = 0;//从0开始重新计算
                index = i + 1;//index从i+1开始
            }
        }
        if (totalSum < 0) return -1;//数组加和小于零一定跑不完,否则一定跑的完
        return index;//从index开始能跑完
    }
}

135. 分发糖果

力扣题目链接

老师想给孩子们分发糖果,有 N 个孩子站成了一条直线,老师会根据每个孩子的表现,预先给他们评分。

你需要按照以下要求,帮助老师给这些孩子分发糖果:

  • 每个孩子至少分配到 1 个糖果。

  • 相邻的孩子中,评分高的孩子必须获得更多的糖果。

那么这样下来,老师至少需要准备多少颗糖果呢?

示例 1:

  • 输入: [1,0,2]

  • 输出: 5

  • 解释: 你可以分别给这三个孩子分发 2、1、2 颗糖果。

示例 2:

  • 输入: [1,2,2]

  • 输出: 4

  • 解释: 你可以分别给这三个孩子分发 1、2、1 颗糖果。第三个孩子只得到 1 颗糖果,这已满足上述两个条件。

思路

  • 需要遍历两次,一次不好考虑

  • 先从左向右遍历,如果右边比左边分高,那就是左边的糖果+1,其他情况都给一个糖果

  • 然后从右向左遍历,如果左边比右边分高,那就要更新糖果,取原来的和右边糖果+1最大的(注意一定要取最大的)

  • 最后计算数组元素的和即可

代码

class Solution {
    public int candy(int[] ratings) {
        int[] candyVec = new int[ratings.length];
        for (int i = 0; i < ratings.length; i++){//从左向右遍历
            if (i > 0 && ratings[i] > ratings[i - 1])//比左边大
                candyVec[i] = candyVec[i - 1] + 1;//左边的+1
            else {
                candyVec[i] = 1;//其他情况都给1个糖果
            }
        }
        for (int i = ratings.length - 1; i >= 0; i--){//从右向左遍历
            if (i < ratings.length - 1 && ratings[i] > ratings[i + 1])//比右边大
                candyVec[i] = Math.max(candyVec[i],candyVec[i + 1] + 1);//取原来的和右边糖果数+1更大的
        }
        int sum = 0;
        for (int candy : candyVec){
            sum += candy;
        }
        return sum;//返回candyVec数组元素的和
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 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、付费专栏及课程。

余额充值