Day34数据结构与算法(Java) 贪心算法:K次取反后最大化的数组和 ,加油站,分发糖果

目录

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

贪心的思路,局部最优:

二、134. 加油站

方法一

方法二

三、135. 分发糖果


一、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。

如图: 134.加油站

 

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 颗糖果。

那么本题我采用了两次贪心的策略:

  • 一次是从左到右遍历,只比较右边孩子评分比左边大的情况。
  • 一次是从右到左遍历,只比较左边孩子评分比右边大的情况。

这样从局部最优推出了全局最优,即:相邻的孩子中,评分高的孩子获得更多的糖果。

如图:

135.分发糖果1

所以该过程代码如下:

 

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;
    }
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值