代码随想录算法训练营第34天 |1005.K次取反后最大化的数组和 134. 加油站 135. 分发糖果

news2024/11/16 2:19:59

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

题目链接:1005.K次取反后最大化的数组和

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

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

示例 1:

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

示例 2:

  • 输入:A = [3,-1,0,2], K = 3
  • 输出:6
  • 解释:选择索引 (1, 2, 2) ,然后 A 变为 [3,1,0,2]。

示例 3:

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

提示:

  • 1 <= A.length <= 10000
  • 1 <= K <= 10000
  • -100 <= A[i] <= 100

💡解题思路

贪心的思路,局部最优:让绝对值大的负数变为正数,当前数值达到最大,整体最优:整个数组和达到最大。

局部最优可以推出全局最优。

那么如果将负数都转变为正数了,K依然大于0,此时的问题是一个有序正整数序列,如何转变K次正负,让 数组和 达到最大。

那么又是一个贪心:局部最优:只找数值最小的正整数进行反转,当前数值和可以达到最大(例如正整数数组{5, 3, 1},反转1 得到-1 比 反转5得到的-5 大多了),全局最优:整个 数组和 达到最大。

那么本题的解题步骤为:

  • 第一步:将数组按照绝对值大小从大到小排序,注意要按照绝对值的大小
  • 第二步:从前向后遍历,遇到负数将其变为正数,同时K--
  • 第三步:如果K还大于0,那么反复转变数值最小的元素,将K用完
  • 第四步:求和

💻实现代码

nums = IntStream.of(nums)
.boxed()
.sorted((o1, o2) -> Math.abs(o2) - Math.abs(o1))
.mapToInt(Integer::intValue).toArray();

Java按照绝对值大小排列

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();
	int len = nums.length;	    
	for (int i = 0; i < len; i++) {
	    //从前向后遍历,遇到负数将其变为正数,同时K--
	    if (nums[i] < 0 && K > 0) {
	    	nums[i] = -nums[i];
	    	K--;
	    }
	}
	// 如果K还大于0,那么反复转变数值最小的元素,将K用完

	if (K % 2 == 1) nums[len - 1] = -nums[len - 1];
	return Arrays.stream(nums).sum();

    }
}
class Solution {
    public int largestSumAfterKNegations(int[] nums, int k) {
        Arrays.sort(nums);
        int i = 0;

        while (i < nums.length && nums[i] < 0 && k > 0) {
            nums[i] = -nums[i];
            i++;
            k--;
        }

        if (k == 0 || (i < nums.length && nums[i] == 0) ) {
            return countSum(nums);
        }

        Arrays.sort(nums);
        if (k % 2 == 0) {
            return countSum(nums);
        } else {
            return countSum(nums) - 2 * nums[0];
        }
    }

    private int countSum(int[] nums) {
        int sum = 0;
        for (int num : nums) {
            sum += num;
        }

        return sum;
    }
}

134. 加油站

题目链接:134. 加油站

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

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

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

说明:

  • 如果题目有解,该答案即为唯一答案。
  • 输入数组均为非空数组,且长度相同。
  • 输入数组中的元素均为非负数。

示例 1: 输入:

  • gas = [1,2,3,4,5]
  • cost = [3,4,5,1,2]

输出: 3 解释:

  • 从 3 号加油站(索引为 3 处)出发,可获得 4 升汽油。此时油箱有 = 0 + 4 = 4 升汽油
  • 开往 4 号加油站,此时油箱有 4 - 1 + 5 = 8 升汽油
  • 开往 0 号加油站,此时油箱有 8 - 2 + 1 = 7 升汽油
  • 开往 1 号加油站,此时油箱有 7 - 3 + 2 = 6 升汽油
  • 开往 2 号加油站,此时油箱有 6 - 4 + 3 = 5 升汽油
  • 开往 3 号加油站,你需要消耗 5 升汽油,正好足够你返回到 3 号加油站。
  • 因此,3 可为起始索引。

示例 2: 输入:

  • gas = [2,3,4]

  • cost = [3,4,3]

  • 输出: -1

  • 解释: 你不能从 0 号或 1 号加油站出发,因为没有足够的汽油可以让你行驶到下一个加油站。我们从 2 号加油站出发,可以获得 4 升汽油。 此时油箱有 = 0 + 4 = 4 升汽油。开往 0 号加油站,此时油箱有 4 - 3 + 2 = 3 升汽油。开往 1 号加油站,此时油箱有 3 - 3 + 3 = 3 升汽油。你无法返回 2 号加油站,因为返程需要消耗 4 升汽油,但是你的油箱只有 3 升汽油。因此,无论怎样,你都不可能绕环路行驶一周。

💡解题思路

贪心算法(方法一)

直接从全局进行贪心选择,情况如下:

  • 情况一:如果gas的总和小于cost总和,那么无论从哪里出发,一定是跑不了一圈的

  • 情况二:rest[i] = gas[i]-cost[i]为一天剩下的油,i从0开始计算累加到最后一站,如果累加没有出现负数,说明从0出发,油就没有断过,那么0就是起点。

  • 情况三:如果累加的最小值是负数,汽车就要从非0节点出发,从后向前,看哪个节点能把这个负数填平,能把这个负数填平的节点就是出发节点。

贪心算法(方法二)

可以换一个思路,首先如果总油量减去总消耗大于等于零那么一定可以跑完一圈,说明 各个站点的加油站 剩油量rest[i]相加一定是大于等于零的。

每个加油站的剩余量rest[i]为gas[i] - cost[i]。

i从0开始累加rest[i],和记为curSum,一旦curSum小于零,说明[0, i]区间都不能作为起始位置,因为这个区间选择任何一个位置作为起点,到i这里都会断油,那么起始位置从i+1算起,再从0计算curSum。

如图:

那么为什么一旦[0,i] 区间和为负数,起始位置就可以是i+1呢,i+1后面就不会出现更大的负数?

如果出现更大的负数,就是更新i,那么起始位置又变成新的i+1了。

那有没有可能 [0,i] 区间 选某一个作为起点,累加到 i这里 curSum是不会小于零呢? 如图:

如果 curSum<0 说明 区间和1 + 区间和2 < 0, 那么 假设从上图中的位置开始计数curSum不会小于0的话,就是 区间和2>0。

区间和1 + 区间和2 < 0 同时 区间和2>0,只能说明区间和1 < 0, 那么就会从假设的箭头初就开始从新选择其实位置了。

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

💻实现代码

// 解法1
class Solution {
    public int canCompleteCircuit(int[] gas, int[] cost) {
        int sum = 0;
        int min = 0;
        for (int i = 0; i < gas.length; i++) {
            sum += (gas[i] - cost[i]);
            min = Math.min(sum, min);
        }

        if (sum < 0) return -1;
        if (min >= 0) return 0;

        for (int i = gas.length - 1; i > 0; i--) {
            min += (gas[i] - cost[i]);
            if (min >= 0) return i;
        }

        return -1;
    }
}

// 解法2
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 += gas[i] - cost[i];
            totalSum += gas[i] - cost[i];
            if (curSum < 0) {
                index = (i + 1) % gas.length ; 
                curSum = 0;
            }
        }
        if (totalSum < 0) return -1;
        return index;
    }
}

135. 分发糖果

题目链接:135. 分发糖果

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

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

  • 每个孩子至少分配到 1 个糖果。
  • 相邻的孩子中,评分高的孩子必须获得更多的糖果。

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

示例 1:

  • 输入: [1,0,2]
  • 输出: 5
  • 解释: 你可以分别给这三个孩子分发 2、1、2 颗糖果。

示例 2:

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

💡解题思路

这道题目一定是要确定一边之后,再确定另一边,例如比较每一个孩子的左边,然后再比较右边,如果两边一起考虑一定会顾此失彼

先确定右边评分大于左边的情况(也就是从前向后遍历)

此时局部最优:只要右边评分比左边大,右边的孩子就多一个糖果,全局最优:相邻的孩子中,评分高的右孩子获得比左边孩子更多的糖果

局部最优可以推出全局最优。

如果ratings[i] > ratings[i - 1] 那么[i]的糖 一定要比[i - 1]的糖多一个,所以贪心:candyVec[i] = candyVec[i - 1] + 1

如图:

135.分发糖果

再确定左孩子大于右孩子的情况(从后向前遍历)

遍历顺序这里有同学可能会有疑问,为什么不能从前向后遍历呢?

因为 rating[5]与rating[4]的比较 要利用上 rating[5]与rating[6]的比较结果,所以 要从后向前遍历。

如果从前向后遍历,rating[5]与rating[4]的比较 就不能用上 rating[5]与rating[6]的比较结果了 。如图:

所以确定左孩子大于右孩子的情况一定要从后向前遍历!

如果 ratings[i] > ratings[i + 1],此时candyVec[i](第i个小孩的糖果数量)就有两个选择了,一个是candyVec[i + 1] + 1(从右边这个加1得到的糖果数量),一个是candyVec[i](之前比较右孩子大于左孩子得到的糖果数量)。

那么又要贪心了,局部最优:取candyVec[i + 1] + 1 和 candyVec[i] 最大的糖果数量,保证第i个小孩的糖果数量既大于左边的也大于右边的。全局最优:相邻的孩子中,评分高的孩子获得更多的糖果。

局部最优可以推出全局最优。

所以就取candyVec[i + 1] + 1 和 candyVec[i] 最大的糖果数量,candyVec[i]只有取最大的才能既保持对左边candyVec[i - 1]的糖果多,也比右边candyVec[i + 1]的糖果多

如图:

135.分发糖果1

所以该过程代码如下:

// 从后向前
for (int i = ratings.size() - 2; i >= 0; i--) {
    if (ratings[i] > ratings[i + 1] ) {
        candyVec[i] = max(candyVec[i], candyVec[i + 1] + 1);
    }
}

💻实现代码

class Solution {
    /**
         分两个阶段
         1、起点下标1 从左往右,只要 右边 比 左边 大,右边的糖果=左边 + 1
         2、起点下标 ratings.length - 2 从右往左, 只要左边 比 右边 大,此时 左边的糖果应该 取本身的糖果数(符合比它左边大) 和 右边糖果数 + 1 二者的最大值,这样才符合 它比它左边的大,也比它右边大
    */
    public int candy(int[] ratings) {
        int len = ratings.length;
        int[] candyVec = new int[len];
        candyVec[0] = 1;
        for (int i = 1; i < len; i++) {
            candyVec[i] = (ratings[i] > ratings[i - 1]) ? candyVec[i - 1] + 1 : 1;
        }

        for (int i = len - 2; i >= 0; i--) {
            if (ratings[i] > ratings[i + 1]) {
                candyVec[i] = Math.max(candyVec[i], candyVec[i + 1] + 1);
            }
        }

        int ans = 0;
        for (int num : candyVec) {
            ans += num;
        }
        return ans;
    }
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1402815.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【明道云】学习笔记1-了解APaaS

【背景】 APaaS (Application Platform As A Service) &#xff0c;即应用程序平台即服务&#xff0c;这是基于PaaS&#xff08;平台即服务&#xff09;的一种解决方案&#xff0c;支持应用程序在云端的开发、部署和运行&#xff0c;提供软件开发中的基础工具给用户&#xff0…

【JavaEE进阶】 Spring Boot⽇志

文章目录 &#x1f38b;关于日志&#x1f6a9;为什么要学习⽇志&#x1f6a9;⽇志的⽤途&#x1f6a9;日志的简单使用 &#x1f384;打印⽇志&#x1f6a9;程序中得到⽇志对象&#x1f6a9;使⽤⽇志对象打印⽇志 &#x1f38d;⽇志格式的说明&#x1f6a9;⽇志级别的作用&#…

Java - 深入理解加密解密和签名算法

文章目录 应用的接口安全性问题可能来源加密解密Why保护数据隐私防止未经授权的访问防止数据泄露 对称加密 VS 单向加密 VS 非对称加密一、对称加密二、单向加密&#xff08;哈希加密&#xff09;三、非对称加密 常用的对称加密算法1. AES&#xff08;高级加密标准&#xff09;…

【Java】学习一门开发语言,从TA的Hello World开始

欢迎来到《小5讲堂》 大家好&#xff0c;我是全栈小5。 这是《Java》序列文章&#xff0c;每篇文章将以博主理解的角度展开讲解&#xff0c; 特别是针对知识点的概念进行叙说&#xff0c;大部分文章将会对这些概念进行实际例子验证&#xff0c;以此达到加深对知识点的理解和掌握…

第15届蓝桥杯嵌入式省赛准备第二天总结笔记(使用STM32cubeMX创建hal库工程+按键输入)

一.查看电路图 按键是使用的PB0,PB1,PB2,PA0四个引脚&#xff0c;然后使用CubeMX配置引脚&#xff0c;4个脚都配置为输入模式和上拉。 程序生成之后把不用的删掉&#xff0c;需要的留下&#xff0c;这里我把函数名改了。 然后写按键扫描读取程序&#xff0c;这里参考的正点原子…

【Linux】—— 命名管道详解

命名管道是一种在操作系统中用于进程间通信的机制&#xff0c;它允许不同的进程之间通过管道进行数据交换。与匿名管道相比&#xff0c;命名管道具有更多的灵活性和功能。在本博客中&#xff0c;我们将深入探讨命名管道的概念、用途以及如何在编程中使用它们。 目录 &#xff…

【cucumber】cucumber-reporting生成测试报告

原始的cucumber report 比较粗糙 我们可以通过cucumber-reporting 插件对报告进去优化 在pom.xml里面添加cuccumber-reporting 插件 <!-- 根据 cucumber json文件 美化测试报告--><dependency><groupId>net.masterthought</groupId><artifactId>…

何为PyTorch?

PyTorch的名字来源于它的功能和设计哲学。"Py"代表Python&#xff0c;因为PyTorch是一个基于Python的深度学习库&#xff0c;它充分利用了Python语言的灵活性和易用性&#xff0c;为开发者提供了简洁而强大的接口。“Torch”则代表其前身—— Torch&#xff0c;这是一…

瓦片地图游戏开发的底层代码

原理&#xff1a; 二维数组存储每个瓦片序号 然后有一个缓冲区存储瓦片图片&#xff0c; 最后连续采样缓冲区&#xff0c;粘贴到屏幕上&#xff0c; 而缓冲区数据随着采样越界再重新更新 #include <graphics.h> #include <stdio.h>// 默认游戏地图 int map[20…

使用vue-pdf插件加载pdf

安装&#xff1a; // 安装这个版本&#xff0c;其它版本会有千奇百怪的错&#xff0c;这个版本和4.0.0都是可以的 cnpm install vue-pdf4.2.0// 安装pdfjs-dist cnpm install pdfjs-dist2.5.207 使用&#xff1a; // 我的css样式是pxToRem&#xff0c;友友们使用可能样式会有…

二叉树题目:二叉树的序列化与反序列化

文章目录 题目标题和出处难度题目描述要求示例数据范围 前言解法一思路和算法代码复杂度分析 解法二思路和算法代码复杂度分析 题目 标题和出处 标题&#xff1a;二叉树的序列化与反序列化 出处&#xff1a;297. 二叉树的序列化与反序列化 难度 8 级 题目描述 要求 序列…

大数据开发之Scala

第 1 章&#xff1a;scala入门 1.1 概述 scala将面向对象和函数式编程结合成一种简洁的高级语言 特点 1、scala和java一样属于jvm语言&#xff0c;使用时都需要先编译为class字节码文件&#xff0c;并且scala能够直接调用java的类库 2、scala支持两种编程范式面向对象和函数式…

MySQL索引优化:深入理解索引下推原理与实践

随着MySQL的不断发展和升级&#xff0c;每个版本都为数据库性能和查询优化带来了新的特性。在MySQL 5.6中&#xff0c;引入了一个重要的优化特性——索引下推&#xff08;Index Condition Pushdown&#xff0c;简称ICP&#xff09;。ICP能够在某些查询场景下显著提高查询性能&a…

JVM系列-3.类的生命周期

&#x1f44f;作者简介&#xff1a;大家好&#xff0c;我是爱吃芝士的土豆倪&#xff0c;24届校招生Java选手&#xff0c;很高兴认识大家&#x1f4d5;系列专栏&#xff1a;Spring原理、JUC原理、Kafka原理、分布式技术原理、数据库技术、JVM原理&#x1f525;如果感觉博主的文…

想找一个没有中间商的账户,很简单,昂首资本推给你

各位投资者都知道&#xff0c;交易的成本决定我们是否盈利&#xff0c;那么有没有一个没有中间商的账户呢&#xff1f;当然有了&#xff0c;昂首资本这就推给你。 在交易的时候&#xff0c;银行为投资者提供兑换业务&#xff0c;并从中收取费用。至于经纪商&#xff0c;它是交…

爬虫requests+综合练习详解

Day2 - 1.requests第一血_哔哩哔哩_bilibili requests作用&#xff1a;模拟浏览器发请求 requests流程&#xff1a;指定url -> 发起请求 -> 获取响应数据 -> 持续化存储 爬取搜狗首页的页面数据 import requests# 指定url url https://sogou.com # 发起请求 resp…

GPS位置虚拟软件 AnyGo mac激活版

AnyGo for Mac是一款一键将iPhone的GPS位置更改为任何位置的强大软件&#xff01;使用AnyGo在其iOS或Android设备上改变其GPS位置&#xff0c;并在任何想要的地方显示自己的位置。这对那些需要测试应用程序、游戏或其他依赖于地理位置信息的应用程序的开发人员来说非常有用&…

idea远程服务调试

1. 配置idea远程服务调试 这里以 idea 新 ui 为例&#xff0c;首先点击上面的 debug 旁边的三个小圆点&#xff0c;然后在弹出的框框中选择 “Edit”&#xff0c;如下图所示。 然后进入到打开的界面后&#xff0c;点击左上角的 “” 进行添加&#xff0c;找到 “Remote JVM De…

Java 面向对象 04 构造方法(黑马)

这是以前没有赋值的时候&#xff0c;在&#xff08;&#xff09;里面是空的&#xff1a; 代码&#xff1a; 左边的是调用的空参构造&#xff0c;但是右边没有写空参构造&#xff0c;并不会报错&#xff0c;因为虚拟机会自己给外面一个空参构造的方法&#xff0c;就是这样的&am…

详谈c++智能指针!!!

文章目录 前言一、智能指针的发展历史1.C 98/03 的尝试——std::auto_ptr2.std::unique_ptr3.std::shared_ptr4.std::weak_ptr5.智能指针的大小6.智能指针使用注意事项 二、智能指针的模拟实现三、C11和boost中智能指针的关系 前言 C/C 语言最为人所诟病的特性之一就是存在内存…