代码随想录 ゞ 浴缸里的玫瑰 2024-02-22 02:25 39阅读 0赞 ## 前言 ## 代码随想录算法训练营day34 -------------------- ## 一、Leetcode 1005.K次取反后最大化的数组和 ## ### 1.题目 ### 给你一个整数数组 nums 和一个整数 k ,按以下方法修改该数组: css 复制代码 `选择某个下标 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\] 。 来源:力扣(LeetCode) 链接:[leetcode.cn/problems/ma…][leetcode.cn_problems_ma] ### 2.解题思路 ### 方法一:从小到大修改每个负数 思路与算法 由于我们希望数组的和尽可能大,因此除非万不得已,我们应当总是修改负数,并且优先修改值最小的负数。因为将负数 −x−x 修改成 xx 会使得数组的和增加 2x2x,所以这样的贪心操作是最优的。 当给定的 KK 小于等于数组中负数的个数时,我们按照上述方法从小到大依次修改每一个负数即可。但如果 KK 的值较大,那么我们不得不去修改非负数(即正数或者 00)了。由于修改 00 对数组的和不会有影响,而修改正数会使得数组的和减小,因此: 复制代码 `如果数组中存在 00,那么我们可以对它进行多次修改,直到把剩余的修改次数用完; 如果数组中不存在 00 并且剩余的修改次数是偶数,由于对同一个数修改两次等价于不进行修改,因此我们也可以在不减小数组的和的前提下,把修改次数用完; 如果数组中不存在 00 并且剩余的修改次数是奇数,那么我们必然需要使用单独的一次修改将一个正数变为负数(剩余的修改次数为偶数,就不会减小数组的和)。为了使得数组的和尽可能大,我们就选择那个最小的正数。 需要注意的是,在之前将负数修改为正数的过程中,可能出现了(相较于原始数组中最小的正数)更小的正数,这一点不能忽略。` 细节 为了实现上面的算法,我们可以对数组进行升序排序,首先依次遍历每一个负数(将负数修改为正数),再遍历所有的数(将 00 或最小的正数进行修改)。 然而注意到本题中数组元素的范围为 \[−100,100\]\[−100,100\],因此我们可以使用计数数组(桶)或者哈希表,直接统计每个元素出现的次数,再升序遍历元素的范围,这样就省去了排序需要的时间。 ### 3.代码实现 ### java 复制代码 `class Solution { public int largestSumAfterKNegations(int[] nums, int k) { Map<Integer, Integer> freq = new HashMap<Integer, Integer>(); for (int num : nums) { freq.put(num, freq.getOrDefault(num, 0) + 1); } int ans = Arrays.stream(nums).sum(); for (int i = -100; i < 0; ++i) { if (freq.containsKey(i)) { int ops = Math.min(k, freq.get(i)); ans += (-i) * ops * 2; freq.put(i, freq.get(i) - ops); freq.put(-i, freq.getOrDefault(-i, 0) + ops); k -= ops; if (k == 0) { break; } } } if (k > 0 && k % 2 == 1 && !freq.containsKey(0)) { for (int i = 1; i <= 100; ++i) { if (freq.containsKey(i)) { ans -= i * 2; break; } } } return ans; } }` ## 二、Leetcode 134. 加油站 ## ### 1.题目 ### 在一条环路上有 n 个加油站,其中第 i 个加油站有汽油 gas\[i\] 升。 你有一辆油箱容量无限的的汽车,从第 i 个加油站开往第 i+1 个加油站需要消耗汽油 cost\[i\] 升。你从其中的一个加油站出发,开始时油箱为空。 给定两个整数数组 gas 和 cost ,如果你可以按顺序绕环路行驶一周,则返回出发时加油站的编号,否则返回 -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 升汽油。 因此,无论怎样,你都不可能绕环路行驶一周。 提示: ini 复制代码 `gas.length == n cost.length == n 1 <= n <= 105 0 <= gas[i], cost[i] <= 104` ### 2.解题思路 ### 方法一:一次遍历 思路与算法 最容易想到的解法是:从头到尾遍历每个加油站,并检查以该加油站为起点,最终能否行驶一周。我们可以通过减小被检查的加油站数目,来降低总的时间复杂度。 假设我们此前发现,从加油站 xx 出发,每经过一个加油站就加一次油(包括起始加油站),最后一个可以到达的加油站是 yy(不妨设 x<yx<y)。这就说明: ∑i=xygas\[i\]<∑i=xycost\[i\]∑i=xjgas\[i\]≥∑i=xjcost\[i\] (For all j∈\[x,y)) i=x∑ygas\[i\]<i=x∑ycost\[i\]i=x∑jgas\[i\]≥i=x∑jcost\[i\] (For all j∈\[x,y)) 第一个式子表明无法到达加油站 yy 的下一个加油站,第二个式子表明可以到达 yy 以及 yy 之前的所有加油站。 现在,考虑任意一个位于 x,yx,y 之间的加油站 zz(包括 xx 和 yy),我们现在考察从该加油站出发,能否到达加油站 yy 的下一个加油站,也就是要判断 ∑i=zygas\[i\]∑i=zygas\[i\] 与 ∑i=zycost\[i\]∑i=zycost\[i\] 之间的大小关系。 根据上面的式子,我们得到: ∑i=zygas\[i\]=∑i=xygas\[i\]−∑i=xz−1gas\[i\]<∑i=xycost\[i\]−∑i=xz−1gas\[i\]<∑i=xycost\[i\]−∑i=xz−1cost\[i\]=∑i=zycost\[i\]i=z∑ygas\[i\]=i=x∑ygas\[i\]−i=x∑z−1gas\[i\]<i=x∑ycost\[i\]−i=x∑z−1gas\[i\]<i=x∑ycost\[i\]−i=x∑z−1cost\[i\]=i=z∑ycost\[i\] 其中不等式的第二步、第三步分别利用了上面的第一个、第二个不等式。 从上面的推导中,能够得出结论:从 x,yx,y 之间的任何一个加油站出发,都无法到达加油站 yy 的下一个加油站。 在发现了这一个性质后,算法就很清楚了:我们首先检查第 00 个加油站,并试图判断能否环绕一周;如果不能,就从第一个无法到达的加油站开始继续检查。 ### 3.代码实现 ### java 复制代码 `class Solution { public int canCompleteCircuit(int[] gas, int[] cost) { int n = gas.length; int i = 0; while (i < n) { int sumOfGas = 0, sumOfCost = 0; int cnt = 0; while (cnt < n) { int j = (i + cnt) % n; sumOfGas += gas[j]; sumOfCost += cost[j]; if (sumOfCost > sumOfGas) { break; } cnt++; } if (cnt == n) { return i; } else { i = i + cnt + 1; } } return -1; } }` ## 三、Leetcode 135. 分发糖果 ## ### 1.题目 ### n 个孩子站成一排。给你一个整数数组 ratings 表示每个孩子的评分。 你需要按照以下要求,给这些孩子分发糖果: 复制代码 `每个孩子至少分配到 1 个糖果。 相邻两个孩子评分更高的孩子会获得更多的糖果。` 请你给每个孩子分发糖果,计算并返回需要准备的 最少糖果数目 。 示例 1: 输入:ratings = \[1,0,2\] 输出:5 解释:你可以分别给第一个、第二个、第三个孩子分发 2、1、2 颗糖果。 示例 2: 输入:ratings = \[1,2,2\] 输出:4 解释:你可以分别给第一个、第二个、第三个孩子分发 1、2、1 颗糖果。 第三个孩子只得到 1 颗糖果,这满足题面中的两个条件。 提示: ini 复制代码 `n == ratings.length 1 <= n <= 2 * 104 0 <= ratings[i] <= 2 * 104` 来源:力扣(LeetCode) 链接:[leetcode.cn/problems/ca…][leetcode.cn_problems_ca] ### 2.解题思路 ### 方法一:两次遍历 思路及解法 我们可以将「相邻的孩子中,评分高的孩子必须获得更多的糖果」这句话拆分为两个规则,分别处理。 css 复制代码 `左规则:当 ratings[i−1]<ratings[i]ratings[i−1]<ratings[i] 时,ii 号学生的糖果数量将比 i−1i−1 号孩子的糖果数量多。 右规则:当 ratings[i]>ratings[i+1]ratings[i]>ratings[i+1] 时,ii 号学生的糖果数量将比 i+1i+1 号孩子的糖果数量多。` 我们遍历该数组两次,处理出每一个学生分别满足左规则或右规则时,最少需要被分得的糖果数量。每个人最终分得的糖果数量即为这两个数量的最大值。 具体地,以左规则为例:我们从左到右遍历该数组,假设当前遍历到位置 ii,如果有 ratings\[i−1\]<ratings\[i\]ratings\[i−1\]<ratings\[i\] 那么 ii 号学生的糖果数量将比 i−1i−1 号孩子的糖果数量多,我们令 left\[i\]=left\[i−1\]+1left\[i\]=left\[i−1\]+1 即可,否则我们令 left\[i\]=1left\[i\]=1。 在实际代码中,我们先计算出左规则 leftleft 数组,在计算右规则的时候只需要用单个变量记录当前位置的右规则,同时计算答案即可。 ### 3.代码实现 ### java 复制代码 `class Solution { public int candy(int[] ratings) { int n = ratings.length; int[] left = new int[n]; for (int i = 0; i < n; i++) { if (i > 0 && ratings[i] > ratings[i - 1]) { left[i] = left[i - 1] + 1; } else { left[i] = 1; } } int right = 0, ret = 0; for (int i = n - 1; i >= 0; i--) { if (i < n - 1 && ratings[i] > ratings[i + 1]) { right++; } else { right = 1; } ret += Math.max(left[i], right); } return ret; } }` [leetcode.cn_problems_ma]: https://link.juejin.cn?target=https%3A%2F%2Fleetcode.cn%2Fproblems%2Fmaximize-sum-of-array-after-k-negations [leetcode.cn_problems_ca]: https://link.juejin.cn?target=https%3A%2F%2Fleetcode.cn%2Fproblems%2Fcandy
相关 代码随想录 前言 代码随想录算法训练营day44 -------------------- 一、Leetcode 518. 零钱兑换 II 1.题目 给你一个整数数组 布满荆棘的人生/ 2024年02月22日 09:45/ 0 赞/ 52 阅读
相关 代码随想录 前言 代码随想录算法训练营day14 -------------------- 一、Leetcode \\\\ 144. 二叉树的前序遍历 1.题目 给你 待我称王封你为后i/ 2024年02月22日 09:16/ 0 赞/ 44 阅读
相关 代码随想录 一、Leetcode 20. 有效的括号 1.题目 给定一个只包括 '(',')','\{','\}','\[','\]' 的字符串 s ,判断字符串是否有效。 ﹏ヽ暗。殇╰゛Y/ 2024年02月22日 09:15/ 0 赞/ 61 阅读
相关 代码随想录 前言 代码随想录算法训练营day06 -------------------- 一、哈希表基础知识 【 [代码随想录][Link 1]】 【[菜鸟教 桃扇骨/ 2024年02月22日 09:14/ 0 赞/ 47 阅读
相关 代码随想录 前言 代码随想录算法训练营day39 -------------------- 一、Leetcode 62.不同路径 1.题目 一个机器人位于一个 m x 女爷i/ 2024年02月22日 04:39/ 0 赞/ 81 阅读
相关 代码随想录 前言 代码随想录算法训练营day37 -------------------- 一、Leetcode 968.监控二叉树 v 1.题目 给定一个二叉树,我 朱雀/ 2024年02月22日 04:38/ 0 赞/ 108 阅读
相关 代码随想录 前言 代码随想录算法训练营day37 -------------------- 一、Leetcode 968.监控二叉树 v 1.题目 给定一个二叉树,我 叁歲伎倆/ 2024年02月22日 04:37/ 0 赞/ 62 阅读
相关 代码随想录 前言 代码随想录算法训练营day39 -------------------- 一、Leetcode 62.不同路径 1.题目 一个机器人位于一个 m x 左手的ㄟ右手/ 2024年02月22日 02:29/ 0 赞/ 49 阅读
相关 代码随想录 前言 代码随想录算法训练营day35 -------------------- 一、Leetcode 860.柠檬水找零 1.题目 在柠檬水摊上,每一杯柠 朱雀/ 2024年02月22日 02:26/ 0 赞/ 114 阅读
相关 代码随想录 前言 代码随想录算法训练营day34 -------------------- 一、Leetcode 1005.K次取反后最大化的数组和 1.题目 给你一 ゞ 浴缸里的玫瑰/ 2024年02月22日 02:25/ 0 赞/ 40 阅读
还没有评论,来说两句吧...