当前位置: 首页 > article >正文

LeetCode刷题日记之贪心算法(三)

目录

  • 前言
  • K次取反后最大化的数组和
  • 加油站
  • 分发糖果
  • 总结


前言

经过前两篇的学习,相信大家对贪心算法的理解逐渐加深。这篇文章将继续完成几道更具复杂性的贪心题目的学习并快速总结解题过程中的关键步骤。希望博主记录的内容能够为大家提供一些实用的思路✍✍✍


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

LeetCode题目链接

这道题就是给一个整数数组 nums 和一个整数 k ,要求我们经过k次修改使得数组的和最大并返回这个最大和,所谓的修改就是指将数组中任一数取反🤔🤔🤔

请添加图片描述

我们来思路梳理

  • 为了让数组的和尽可能大,我们应该优先选择绝对值最小的负数进行取反,使得负数变成正数,这样会最大化地增加数组的和🤔 如果所有负数都已经被取反完,但仍有操作次数剩余,那么我们可以通过反复取反最小的绝对值元素来减少损失。如果剩余的 k 是偶数,最终结果不变;如果是奇数,则对最小的绝对值元素再取反一次🤔每次选择取反当前最小的负数(或者最小的绝对值元素),这样可以确保每一步都是局部最优,从而达到全局最优

思路梳理的很清楚了,博主直接给出代码

class Solution {
    public int largestSumAfterKNegations(int[] nums, int k) {
        Arrays.sort(nums);//对数组排序便于处理负数和最小绝对值

        //遍历数组将负数取分
        for(int i = 0; i < nums.length && k > 0; i++){
            if(nums[i] < 0){
                nums[i] = -nums[i];//取反负数
                k--;//每取反一次k减少一次
            }
        }

        //如果k仍大于0则处理剩下的k
        if(k % 2 == 1){//奇数则将最小的数取反一次
            int minIndex = findMinIndex(nums);
            nums[minIndex] = -nums[minIndex];
        }

        //计算最终和
        int sum = 0;
        for(int num : nums) sum+= num;

        return sum;

    }

    //找到数组中的最小值索引
    private int findMinIndex(int[] nums){ 
        int minIndex = 0;
        for(int i = 1; i < nums.length; i++){
            if(nums[i] < nums[minIndex]) minIndex = i;
        }
        return minIndex;
    }
}

加油站

LeetCode题目链接

这道题就是给出一个加油站数组gas和一个耗油数组cost,其中gas[i]为i加油站能够获取的汽油,而cost[i]则是指从i加油站到i+1加油站需要消耗的汽油量🤔🤔🤔

请添加图片描述

我们来思路梳理

  • 如果环路上所有加油站提供的油量总和 gas 小于行驶所需的油量总和 cost,则无论从哪里出发都不可能完成一周,直接返回 -1🤔从一个加油站出发,逐步向下一个加油站前进。如果当前加油站的油量不足以开到下一个加油站,就从下一个加油站重新开始尝试🤔 通过局部计算判断从某个加油站开始是否可以行驶一周。如果在某个加油站之前的路段无法到达下一个加油站,则说明从之前的加油站出发都不可能成功,从当前失败的加油站之后开始尝试🤔一旦能绕行一周的加油站找到,则这个解是唯一的

可能逻辑还是不够清楚,我们来进一步梳理贪心四个子逻辑

  • 如何判断最优解( 通过累积 total,判断是否有足够的汽油量来完成一周。如果 total 小于 0,则无论从哪个加油站出发,都不可能绕行一圈🤔)
int total = 0;  // 记录整个环路的总油量差
int tank = 0;   // 记录当前从起始站出发时的油量
int start = 0;  // 记录当前尝试的起始加油站编号
  • 判断局部最优选择的可行性( 对于每个加油站,计算它的油量与到达下一个加油站所需的油量差值 currentFuel。如果当前油量 tank 变成负数,说明从当前起始站无法到达下一个加油站,应该从下一个加油站重新开始🤔)
int currentFuel = gas[i] - cost[i];  // 当前加油站的剩余油量
total += currentFuel;  // 更新整个环路的总油量差
tank += currentFuel;   // 更新当前油箱中的油量
  • 更新问题状态( 当 tank < 0 时,重置起始加油站为 i + 1,同时将油箱清零,这意味着我们从新的起始加油站开始尝试🤔)
if (tank < 0) {
    start = i + 1;  // 更新起始加油站为下一个
    tank = 0;       // 重置油箱
}

完整代码如下

class Solution {
    public int canCompleteCircuit(int[] gas, int[] cost) {
        //初始化总油量、当前油量和起始加油站
        int total = 0;
        int tank = 0;
        int start = 0;

        //遍历每个加油站
        for(int i = 0; i < gas.length; i++){
            int currentFuel = gas[i] - cost[i]; //当前加油站剩余油量
            total += currentFuel;//更新整个环路的总油量差
            tank += currentFuel;//更新当前邮箱的油量

            //判断局部最优选择的可行性
            if(tank < 0){
                start = i + 1;//重新设置起始加油站
                tank = 0;//重置油箱
            }
        }

        return total >= 0? start : -1;//如果总油量差为负则无法完成环路,返回-1
    }
}

可能难以理解的部分

  • 为什么这里只遍历一次即可
    • 我们计算了环路总油量差total,当遍历完加油站后只要环路总油量差不为0说明就是可以绕行一圈的🤔

分发糖果

LeetCode题目链接

这道题就是给一个整数数组ratings表示每个孩子的评分,要求每个孩子都至少分配一个糖果,然后的话相邻评分更高的孩子要获得更多的糖果,然后我们怎么分配的话可能使得分配的糖果数量最小🤔🤔🤔

请添加图片描述

我们来问题梳理

  • 从左到右遍历孩子数组,如果某个孩子的评分比前一个孩子高,那么给他比前一个孩子多一个糖果,这样保证从左往右满足相邻孩子的条件🤔 从右到左再次遍历孩子数组,如果某个孩子的评分比下一个孩子高,并且当前糖果数没有超过下一个孩子的糖果数,那么给他比右边孩子多一个糖果。这样可以确保在从右往左的过程中也满足相邻孩子的要求🤔

逻辑也很清楚,我们直接来编码,完整代码如下

class Solution {
    public int candy(int[] ratings) {
        int n = ratings.length;
        int[] candies = new int[n];//存储每个孩子的糖果数

        for(int i = 0; i < n; i++)candies[i] = 1; //初始化每个孩子至少有一个糖果
        for(int i = 1; i < n; i++){//保证右边评分高的孩子比左边获得更多糖果
            if(ratings[i] > ratings[i - 1])candies[i] = candies[i - 1] + 1;
        }
        for(int i = n - 2; i >= 0; i--){//保证左边评分高的孩子比右边获得更多糖果
            if(ratings[i] > ratings[i + 1])candies[i]=Math.max(candies[i], candies[i + 1] + 1);  // 保证左边糖果数足够
        }

        //计算糖果总数
        int totalCandies = 0;
        for(int candy : candies)totalCandies += candy;

        return totalCandies;
    }
}

总结

通过这几道贪心算法的题目,我们可以看到,贪心算法的核心在于每一步都选择当前最优解,从而达到全局最优。 希望这些记录分享能帮助大家更好地理解贪心算法✊✊✊


http://www.kler.cn/news/357313.html

相关文章:

  • 深入理解WebSocket协议原理、实现与应用
  • 背景全文及翻译
  • 软件工程的学习之详细绪论
  • 学习笔记——交换——STP(生成树)工作原理
  • 机器学习常见概念整理
  • Flink CDC 实时同步mysql数据
  • 现代物流管理:SpringBoot技术突破
  • 数据结构——八大排序(下)
  • 探讨人工智能领域所需学习的高等数学知识及其应用场景,涵盖了微积分、线性代数、概率论等多个数学分支。
  • Ingress-nginx中HTTPS的强制转发
  • WPS 删除重复记录
  • 【最新华为OD机试E卷-支持在线评测】模拟目录管理 (200分)多语言题解-(Python/C/JavaScript/Java/Cpp)
  • OpenGauss学习笔记
  • 基于springboot淮安动物园信息管理系统(源码+定制+开发)动物园数据管理平台、动物园信息系统优化
  • CSS 中的content-visibility属性
  • C语言小游戏--猜数字
  • LabVIEW提高开发效率技巧----用户权限控制
  • Scrapy | 爬取笑话网来认识继承自Spider的crawlspider爬虫类
  • 【Docker】Harbor 私有仓库和管理
  • IEC104规约的秘密之十二----扩展报文之文件断点续传