代码随想录算法Day34 | 1005.K次取反后最大化的数组和 ,134. 加油站 , 135. 分发糖果
1005.K次取反后最大化的数组和
题目链接:1005. K 次取反后最大化的数组和 - 力扣(LeetCode)
思路
贪心思路,局部最优:让绝对值大的负数变为正数,当前数值达到最大,整体最优:整个数组和达到最大。
当数组中的负数全部取反后,K仍然大于0时,这时又是一个贪心:局部最优:只找数值最小的正整数进行反转,当前数值和可以达到最大,全局最优:整个数组和 达到最大。
代码
1 class Solution { 2 public int largestSumAfterKNegations(int[] nums, int K) { 3 // 将数组按照绝对值大小从大到小排序,注意要按照绝对值的大小 4 nums = IntStream.of(nums) 5 .boxed() 6 .sorted((o1, o2) -> Math.abs(o2) - Math.abs(o1)) 7 .mapToInt(Integer::intValue).toArray(); 8 int len = nums.length; 9 for (int i = 0; i < len; i++) { 10 //从前向后遍历,遇到负数将其变为正数,同时K-- 11 if (nums[i] < 0 && K > 0) { 12 nums[i] = -nums[i]; 13 K--; 14 } 15 } 16 // 如果K还大于0,那么反复转变数值最小的元素,将K用完 17 18 if (K % 2 == 1) nums[len - 1] = -nums[len - 1]; 19 return Arrays.stream(nums).sum(); 20 21 } 22 }
1 class Solution { 2 public int largestSumAfterKNegations(int[] A, int K) { 3 if (A.length == 1) return k % 2 == 0 ? A[0] : -A[0]; 4 Arrays.sort(A); 5 int sum = 0; 6 int idx = 0; 7 for (int i = 0; i < K; i++) { 8 if (i < A.length - 1 && A[idx] < 0) { 9 A[idx] = -A[idx]; 10 if (A[idx] >= Math.abs(A[idx + 1])) idx++; 11 continue; 12 } 13 A[idx] = -A[idx]; 14 } 15 16 for (int i = 0; i < A.length; i++) { 17 sum += A[i]; 18 } 19 return sum; 20 } 21 }
134. 加油站
题目链接:134. 加油站 - 力扣(LeetCode)
思路
暴力法
遍历每一个加油站为起点的情况,模拟一圈。
如果跑了一圈,中途没有断油,而且最后油量大于等于0,说明这个起点是ok的。
暴力的方法思路比较简单,但代码写起来也不是很容易,关键是要模拟跑一圈的过程。
for循环适合模拟从头到尾的遍历,而while循环适合模拟环形遍历,要善于使用while!
贪心
解法一
首先如果总油量减去总消耗大于等于零那么一定可以跑完一圈,说明 各个站点的加油站 剩油量rest[i]相加一定是大于等于零的。
每个加油站的剩余量rest[i]为gas[i] - cost[i]。
i从0开始累加rest[i],和记为curSum,一旦curSum小于零,说明[0, i]区间都不能作为起始位置,因为这个区间选择任何一个位置作为起点,到i这里都会断油,那么起始位置从i+1算起,再从0计算curSum。
解法二
直接从全局进行贪心选择,情况如下:
-
情况一:如果gas的总和小于cost总和,那么无论从哪里出发,一定是跑不了一圈的
-
情况二:rest[i] = gas[i]-cost[i]为一天剩下的油,i从0开始计算累加到最后一站,如果累加没有出现负数,说明从0出发,油就没有断过,那么0就是起点。
-
情况三:如果累加的最小值是负数,汽车就要从非0节点出发,从后向前,看哪个节点能把这个负数填平,能把这个负数填平的节点就是出发节点。
代码
1 // 暴力法 2 public int canCompleteCircuit(int[] gas, int[] cost) { 3 for (int i = 0; i < gas.length; i++) { 4 int res = gas[i] - cost[i]; // 记录剩余油量 5 int index = (i + 1) % cost.length; 6 while(res > 0 && index != i){ // 模拟以i为起点行驶一圈(如果有res==0,那么答案就不唯一了) 7 res += gas[index] - cost[index]; 8 index = (index + 1) % cost.length; 9 } 10 // 如果以i为起点跑一圈,剩余油量>=0,返回该起始位置 11 if (res >= 0 && index == i) return i; 12 } 13 return -1; 14 }
1 // 解法1 2 class Solution { 3 public int canCompleteCircuit(int[] gas, int[] cost) { 4 int curSum = 0; 5 int totalSum = 0; 6 int index = 0; 7 for (int i = 0; i < gas.length; i++) { 8 curSum += gas[i] - cost[i]; 9 totalSum += gas[i] - cost[i]; 10 if (curSum < 0) { 11 index = (i + 1) % gas.length ; 12 curSum = 0; 13 } 14 } 15 if (totalSum < 0) return -1; 16 return index; 17 } 18 }
1 // 解法2 2 class Solution { 3 public int canCompleteCircuit(int[] gas, int[] cost) { 4 int sum = 0; 5 int min = 0; 6 for (int i = 0; i < gas.length; i++) { 7 sum += (gas[i] - cost[i]); 8 min = Math.min(sum, min); 9 } 10 11 if (sum < 0) return -1; 12 if (min >= 0) return 0; 13 14 for (int i = gas.length - 1; i > 0; i--) { 15 min += (gas[i] - cost[i]); 16 if (min >= 0) return i; 17 } 18 19 return -1; 20 } 21 }
135. 分发糖果
题目链接:135. 分发糖果 - 力扣(LeetCode)
思路
要确定一边之后,再确定另一边,例如比较每一个孩子的左边,然后再比较右边,如果两边一起考虑一定会顾此失彼。
-
先从前向后遍历数组,确定右边评分大于左边的情况
-
然后从后往前遍历数组,确定左边评分大于右边的情况
注意:
1、确定右边评分大于左边一定要从前往后遍历,而确定左边评分大于右边一定要从后往前遍历,因为是波浪式更新,前面更新的糖果对后面的比较是有影响的。
2、从后往前遍历从 length - 2 开始。
代码
1 class Solution { 2 /** 3 分两个阶段 4 1、起点下标1 从左往右,只要 右边 比 左边 大,右边的糖果=左边 + 1 5 2、起点下标 ratings.length - 2 从右往左, 只要左边 比 右边 大,此时 左边的糖果应该 取本身的糖果数(符合比它左边大) 和 右边糖果数 + 1 二者的最大值,这样才符合 它比它左边的大,也比它右边大 6 */ 7 public int candy(int[] ratings) { 8 int len = ratings.length; 9 int[] candyVec = new int[len]; 10 candyVec[0] = 1; 11 for (int i = 1; i < len; i++) { 12 candyVec[i] = (ratings[i] > ratings[i - 1]) ? candyVec[i - 1] + 1 : 1; 13 } 14 15 for (int i = len - 2; i >= 0; i--) { 16 if (ratings[i] > ratings[i + 1]) { 17 candyVec[i] = Math.max(candyVec[i], candyVec[i + 1] + 1); 18 } 19 } 20 21 int ans = 0; 22 for (int num : candyVec) { 23 ans += num; 24 } 25 return ans; 26 } 27 }

浙公网安备 33010602011771号