目录
一、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]。
贪心的思路,局部最优:
让绝对值大的负数变为正数,当前数值达到最大,整体最优:整个数组和达到最大。
局部最优:只找数值最小的正整数进行反转,当前数值可以达到最大(例如正整数数组{5, 3, 1},反转1 得到-1 比 反转5得到的-5 大多了),全局最优:整个 数组和 达到最大。
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++)
{
if(nums[i]<0&&k>0)
{
nums[i]=-nums[i];
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) {
if(nums.length==1) return k%2==0?nums[0]:-nums[0];
Arrays.sort(nums);
int sum=0;
int idx=0;
for(int i=0;i<k;i++)
{
if(i<nums.length-1&&nums[idx]<0)
{nums[idx]=-nums[idx];
if(nums[idx]>=Math.abs(nums[idx+1])) idx++;
continue;
}
nums[idx]=-nums[idx];
}
for(int i=0;i<nums.length;i++)
{
sum+=nums[i];
}
return sum;
}
}
二、134. 加油站
在一条环路上有 N 个加油站,其中第 i 个加油站有汽油 gas[i] 升。
你有一辆油箱容量无限的的汽车,从第 i 个加油站开往第 i+1 个加油站需要消耗汽油 cost[i] 升。你从其中的一个加油站出发,开始时油箱为空。
如果你可以绕环路行驶一周,则返回出发时加油站的编号,否则返回 -1。
说明:
- 如果题目有解,该答案即为唯一答案。
- 输入数组均为非空数组,且长度相同。
- 输入数组中的元素均为非负数。
示例 1: 输入:
- gas = [1,2,3,4,5]
- cost = [3,4,5,1,2]
方法一
直接从全局进行贪心选择,情况如下:
-
情况一:如果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+1算起,再从0计算curSum。
如图:
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. 分发糖果
老师想给孩子们分发糖果,有 N 个孩子站成了一条直线,老师会根据每个孩子的表现,预先给他们评分。
你需要按照以下要求,帮助老师给这些孩子分发糖果:
- 每个孩子至少分配到 1 个糖果。
- 相邻的孩子中,评分高的孩子必须获得更多的糖果。
那么这样下来,老师至少需要准备多少颗糖果呢?
示例 1:
- 输入: [1,0,2]
- 输出: 5
- 解释: 你可以分别给这三个孩子分发 2、1、2 颗糖果。
那么本题我采用了两次贪心的策略:
- 一次是从左到右遍历,只比较右边孩子评分比左边大的情况。
- 一次是从右到左遍历,只比较左边孩子评分比右边大的情况。
这样从局部最优推出了全局最优,即:相邻的孩子中,评分高的孩子获得更多的糖果。
如图:
所以该过程代码如下:
class Solution {
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;
}
}