代码编织梦想

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

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

题目介绍:

给你一个整数数组 nums 和一个整数 k ,按以下方法修改该数组:

  • 选择某个下标 i 并将 nums[i] 替换为 -nums[i]

重复这个过程恰好 k 次。可以多次选择同一个下标 i

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

示例 1:

输入:nums = [4,2,3], k = 1
输出:5
解释:选择下标 1 ,nums 变为 [4,-2,3] 。

示例 2:

输入:nums = [3,-1,0,2], k = 3
输出:6
解释:选择下标 (1, 2, 2) ,nums 变为 [3,1,0,2] 。

示例 3:

输入:nums = [2,-3,-1,5,-4], k = 2
输出:13
解释:选择下标 (1, 4) ,nums 变为 [2,3,-1,5,4] 。

个人思路:

  • 先将数组排序,然后按起始下标对负数进行反转
    • 负数个数 > k,正常反转完k个,之后遍历剩下的元素即可
    • 负数个数 < k,反转完所有负数后,看看剩余次数为奇数还是偶数
      • 若为偶数,就不需要再反转了
      • 若为奇数,只需要再反转一个正数就行,比较一下此刻元素和上一个元素的绝对值,反转较小的那个即可
class Solution {
    public int largestSumAfterKNegations(int[] nums, int k) {
        int result = 0;
        Arrays.sort(nums);
        int i = 0;
        for (; i < k && i < nums.length; i++) {
            if (nums[i] >= 0)
                break;
            nums[i] = Math.abs(nums[i]);
            result += nums[i];
        }
        if ((k - i) % 2 == 1) {//还剩奇数个反转
            //1.数组已遍历完毕(必然是负数,即其绝对值最小)--->将最后一个反转
            //2.1数组未遍历完毕,比较正负分界处两值绝对值,负数的绝对值较大
            //2.2数组第一个数就是正数,分界处,正数绝对值大
            if (i >= nums.length || i > 0 && nums[i] > nums[i - 1])
                result -= 2 * nums[i - 1];
             else nums[i] *= (-1);
        }
        //遍历剩余元素
        for (; i < nums.length; i++) {
            result += nums[i];
        }
        return result;
    }
}

题解解析:

将数组按绝对值大小进行排序,从大的那一侧遍历,遇到负数就反转

若到末尾还有反转次数,则看次数是奇数还是偶数进行不同操作

奇数则反转最后一个数,偶数则不反转

134. 加油站

题目介绍:

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

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

给定两个整数数组 gascost ,如果你可以绕环路行驶一周,则返回出发时加油站的编号,否则返回 -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 升汽油。
因此,无论怎样,你都不可能绕环路行驶一周。

个人思路:

首先,我们需要使用几个参数去记录一些关键信息

int result = 0;//记录出发的下标
int excess_sum = 0;//遍历全程后,剩下的油
int[] excess_gas = new int[gas.length];//记录每一处加油与油耗的差值
int[] sum_gas = new int[gas.length];//当前下标记录从某个点起至当前点所剩油

然后,我们通过一个for循环遍历整个过程,过程中不断刷新sum_gas,当其为负数时,重新确定出发点,继续循环过程

如果真的能有某个点能循环走完全程,那必然在遍历过程中就碰到;反过来,遍历结束的result不一定就是结果,我们还得看excess_sum是否为非负

class Solution {
    public int canCompleteCircuit(int[] gas, int[] cost) {
        int result = 0;//记录出发的下标
        int excess_sum = 0;//遍历全程后,剩下的油
        int[] excess_gas = new int[gas.length];//记录每一处加油与油耗的差值
        int[] sum_gas = new int[gas.length];//当前下标记录从某个点起至当前点所剩油
        for (int i = 0; i < gas.length; i++) {
            excess_gas[i] = gas[i] - cost[i];
            sum_gas[i] = i == 0 ? excess_gas[i] : excess_gas[i] + sum_gas[i - 1];
            excess_sum += excess_gas[i];
             if (sum_gas[i] < 0) {
                sum_gas[i] = 0;
                result = i + 1;
            }
        }
        return excess_sum >= 0 ? result : -1;
    }
}

题解解析:

法一:一种很巧妙的方法,但并非贪心,了解即可

  • 情况一:如果gas的总和小于cost总和,那么无论从哪里出发,一定是跑不了一圈的
  • 情况二:rest[i] = gas[i]-cost[i]为一天剩下的油,i从0开始计算累加到最后一站,如果累加没有出现负数,说明从0出发,油就没有断过,那么0就是起点。
  • 情况三:如果累加的最小值是负数,汽车就要从非0节点出发,从后向前,看哪个节点能这个负数填平,能把这个负数填平的节点就是出发节点。反过来思考,正序遍历那个点能填平最小的负数,那后面必然可以走完全程,因为整体是够油的
class Solution {
public:
    int canCompleteCircuit(vector<int>& gas, vector<int>& cost) {
        int curSum = 0;
        int min = INT_MAX; // 从起点出发,油箱里的油量最小值
        for (int i = 0; i < gas.size(); i++) {
            int rest = gas[i] - cost[i];
            curSum += rest;
            if (curSum < min) {
                min = curSum;
            }
        }
        if (curSum < 0) return -1;  // 情况1
        if (min >= 0) return 0;     // 情况2
                                    // 情况3
        for (int i = gas.size() - 1; i >= 0; i--) {
            int rest = gas[i] - cost[i];
            min += rest;
            if (min >= 0) {
                return i;
            }
        }
        return -1;
    }
};

法二:和我的想法类似,但是代码优化了不少

class Solution {
    public int canCompleteCircuit(int[] gas, int[] cost) {
        int result = 0;//记录出发的下标
        int excess_sum = 0;//遍历全程后,剩下的油
        int curSum = 0;//当前下标记录从某个点起至当前点所剩油
        for (int i = 0; i < gas.length; i++) {
            curSum += gas[i] - cost[i];
            excess_sum += gas[i] - cost[i];
            if (curSum < 0) {
                curSum = 0;
                result = i + 1;
            }
        }
        return excess_sum >= 0 ? result : -1;
    }
}

135. 分发糖果

题目介绍:

n 个孩子站成一排。给你一个整数数组 ratings 表示每个孩子的评分。

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

  • 每个孩子至少分配到 1 个糖果。
  • 相邻两个孩子评分更高的孩子会获得更多的糖果。

请你给每个孩子分发糖果,计算并返回需要准备的 最少糖果数目

示例 1:

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

示例 2:

输入:ratings = [1,2,2]
输出:4
解释:你可以分别给第一个、第二个、第三个孩子分发 1、2、1 颗糖果。
     第三个孩子只得到 1 颗糖果,这满足题面中的两个条件。

个人思路:

我的想法是标记从起点开始,遍历标记每个点的相对上下位置,下标0处为0,大的就较前一个数+1,小的就-1,

但是相等的处理上较为复杂,想到反单调变化(),但遇到连续很多个孩子一样成绩也要再考虑,时间原因直接看题解了

题解解析:

注意:题目说的是,比相邻的大,就要比他得到的糖果更多,如果相同就没有这个限制。相邻这个条件我们可以分成两边来考虑

我们可以先比较一边再去比较另一边,如果一次遍历同时比较两边一定会失衡。

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

步骤:

  • 我们先从首位起正序遍历,向右比较,得出仅一侧的全局最优,但此时不满足向左比较的条件
  • 再从尾部起逆序遍历,向左比较,也能逐个得到结果,过程中我们要保留与正序遍历的结果相比中较大的结果(其能满足相邻比较的条件)

向左比较不能从首部遍历,不然会重复第一步的过程,没有意义

class Solution {
    public int candy(int[] ratings) {
        int[] candy = new int[ratings.length];

        for (int i = 0; i < candy.length; i++) {
            if (i > 0 && ratings[i] > ratings[i - 1])
                candy[i] = candy[i - 1] + 1;
            else candy[i] = 1;
        }

        for (int i = candy.length - 1; i > 0; i--) {
            if (ratings[i] < ratings[i - 1])
                candy[i - 1] = Integer.max(candy[i] + 1, candy[i - 1]);
        }
        int nums = 0;
        for (int i = 0; i < candy.length; i++) {
            nums += candy[i];
        }
        return nums;
    }
}
版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/m0_63617655/article/details/128860066

代码随想录算法训练营第三十四天| leetcode​1005.k次取反后最大化的数组和134. 加油站135. 分发糖果_冰冰的coco的博客-爱代码爱编程

1005.K次取反后最大化的数组和 题目:力扣 class Solution { public: int largestSumAfterKNegations(vector<int>& nums, int k) { sort(nums.begin(),nums.end());

代码随想录算法训练营day34 | 1005.k次取反后最大化的数组和,134. 加油站,135. 分发糖果_jzh013的博客-爱代码爱编程

1005. K次取反后最大化的数组和 按绝对值大小进行排序,然后再操作 class Solution: def largestSumAfterKNegations(self, nums: List[int], k: int) -> int: nums = sorted(nums, key=abs, reverse=Tru

代码随想录算法训练营第34天|1005.k次取反后最大化的数组和|134. 加油站|135. 分发糖果_永不服输的锐锐米的博客-爱代码爱编程

1005. K 次取反后最大化的数组和  题目描述:给你一个整数数组 nums 和一个整数 k ,按以下方法修改该数组:选择某个下标 i 并将 nums[i] 替换为 -nums[i] 。重复这个过程恰好 k 次。可以多次选择同一个下标 i 。以这种方式修改数组后,返回数组 可能的最大和 。 解题思路:如何让数组的和最大,那么最好的情况就是数组中一个负

代码随想录算法训练营第34天|1005.k次取反后最大化的数组和 、134. 加油站、135. 分发糖果_chauncey1995的博客-爱代码爱编程

代码随想录算法训练营第34天|1005.K次取反后最大化的数组和 、134. 加油站、135. 分发糖果 一. 贪心算法相关题目1005.k次取反后最大化的数组和贪心思路 134.加油站贪心思路

代码随想录算法训练营第三十四天| leetcode1005. k 次取反后最大化的数组和、leetcode134. 加油站、leetcode135. 分发糖果_喵的博客-爱代码爱编程

一、LeetCode1005. K 次取反后最大化的数组和         1:题目描述(1005. K 次取反后最大化的数组和)         给你一个整数数组 nums 和一个整数 k ,按以下方法修改该数组: 选择某个下标 i 并将 nums[i] 替换为 -nums[i] 。         重复这个过程恰好 k 次。可以多次选择同一个下标

代码随想录算法训练营第三十四天|1005. k 次取反后最大化的数组和、134. 加油站、135. 分发糖果-爱代码爱编程

1005. K 次取反后最大化的数组和 思路: 按照绝对值排序,感觉之前某道题也是。 第一步:将数组按照绝对值大小从大到小排序,注意要按照绝对值的大小第二步:从前向后遍历,遇到负数将其变为正数,同时K–第三步:如果K还大

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

1005.K次取反后最大化的数组和 力扣题目链接 思路 方法一 两次贪心第一次,局部最优:将绝对值最大的负数变为正数,当前数值达到最大,全局最优:整个数组和达到最大第二次,局部最优:只找数值最小的正整数进行反转,当前

代码随想录算法训练营第三十四天 | 1005.k次取反后最大化的数组和、134. 加油站、135. 分发糖果-爱代码爱编程

1005.K次取反后最大化的数组和 1005. K 次取反后最大化的数组和 class Solution { static bool cmp(int a,int b){ return abs(a)>abs(

-爱代码爱编程

_29LeetCode代码随想录算法训练营第二十九天-贪心算法 题目列表 1005.K次取反后最大化的数组和134.加油站135.分发糖果 代码随想录地址: https://programmercarl.com/10

代码随想录算法训练营第三十四天| 1005.k次取反后最大化的数组和 、134. 加油站 、135. 分发糖果-爱代码爱编程

Leetcode - 1005 我的思路,首先对列表进行排序,这样绝对值大的负数都会被排在前面,此时我们进行遍历,遇到负数,直接取反,然后k-1,遍历到末尾或者k=0时结束。 结束后若k=0 则返回结果 若不为0,我们需要将k用在绝对值最小的正数上(此时负数都取反了),所以再进行一次排序, 此时分为两种情况,若k为偶数,则k次翻转过后值

代码随想录算法训练营第三十四天-爱代码爱编程

LeetCode 1005.K次取反后最大化的数组和         给定一个整数数组 A,我们只能用以下方法修改该数组:我们选择某个索引 i 并将 A[i] 替换为 -A[i],然后总共重复这个过程 K 次。(我们可以多次选择同一个索引 i。) 以这种方式修改数组后,返回数组可能的最大和。 视频讲解文章讲解https

代码随想录算法训练营第三十七天 | 1005. k 次取反后最大化的数组和,134. 加油站,135. 分发糖果-爱代码爱编程

1005. K 次取反后最大化的数组和 题目链接:1005. K 次取反后最大化的数组和 思路:算是积累了 解题步骤为: 第一步:将数组按照绝对值大小从大到小排序,注意要按照绝对值的大小 第二步:从前向后遍历,遇到负数将

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

1005.K次取反后最大化的数组和 力扣 思路: 1. 局部贪心:让绝对值大的负数变为正数,当前数值达到最大;整体最优:数组和达到最大。 2. 将数组按绝对值从大到小排序。从前往后遍历,当遇到负数时,将其变为正数,k--; 3. 反复转变数值最小的元素,直到k=0。 class Solution { public int largest

代码随想录算法训练营第三十四天| 1005.k次取反后最大化的数组和、134. 加油站、135. 分发糖果-爱代码爱编程

1005.K次取反后最大化的数组和 本题思路很简单,就是将所有负值按照绝对值从大到小依次取反变为正的,当所有负数全部操作完成,k大于零,就将绝对值最小的值取反k次,这样才能保证和的最大化 按照贪心思路: 局部最优:让绝

代码随想录算法训练营第三十四天打卡|1005.k次取反后最大化的数组和,134. 加油站,135. 分发糖果-爱代码爱编程

代码随想录算法训练营第三十四天打卡 1005.K次取反后最大化的数组和134. 加油站135. 分发糖果 1005.K次取反后最大化的数组和 代码 # !/usr/bin/env python

代码随想录算法训练营第三十四天|1005.k次取反后最大化的数组和、134. 加油站、135. 分发糖果-爱代码爱编程

LeetCode 1005.K次取反后最大化的数组和 链接:1005.K次取反后最大化的数组和 思路: 取反后让和最大,很容易想到的策略就是如果有负数,先给负数取反,如果没有负数,则取反最小的那个数,这种策略其实就是贪心法,局部的最优可以导致整体最优,所以这题的难点就是如何维持这种策略。 首先考虑给负数取反,最先取反的负数肯定是最小的那个数,这样取

代码随想录算法训练营第三十四天| 1005.k次取反后最大化的数组和,134. 加油站,135. 分发糖果-爱代码爱编程

1005. Maximize Sum Of Array After K Negations 思路 此题难度是按绝对值排列,先背下来吧先按绝对值大小倒序排列从大到小 如果是负数则取相反数k--如果遍历完毕全为正了K还有富裕 k是否为偶数 如果为偶数 不管如果为奇数 最小值取负值 j

代码随想录算法训练营第三十四日| lc1005.k次取反后最大化的数组和 lc134. 加油站 lc135. 分发糖果_lc100代码-爱代码爱编程

 LC1005.K次取反后最大化的数组和 class Solution: def largestSumAfterKNegations(self, nums: List[int], k: int) -> int: nums = sorted(nums, key = abs, reverse = True) # 将A按绝对值从大

代码随想录算法训练营第三十四天|● 1005.k次取反后最大化的数组和 ● 134. 加油站● 135. 分发糖果-爱代码爱编程

贪心算法: 一、1005.K次取反后最大化的数组和 建议:本题简单一些,估计大家不用想着贪心 ,用自己直觉也会有思路。 题目:给定一个整数数组 A,我们只能用以下方法修改该数组:我们选择某个索引 i 并将 A[i] 替换为 -A[i],然后总共重复这个过程 K 次。(我们可以多次选择同一个索引 i。)