贪心算法每日一题(2)

目录

一、分发饼干

方法一:(小饼干填小肚子)

方法二:(大饼干填大肚子)

二、k次取反后最大化的数组和


 

一、分发饼干

假设你是一位很棒的家长,想要给你的孩子们一些小饼干。但是,每个孩子最多只能给一块饼干。

对每个孩子 i,都有一个胃口值 g[i],这是能让孩子们满足胃口的饼干的最小尺寸;并且每块饼干 j,都有一个尺寸 s[j] 。如果 s[j] >= g[i],我们可以将这个饼干 j 分配给孩子 i ,这个孩子会得到满足。你的目标是尽可能满足越多数量的孩子,并输出这个最大数值。

 

示例 1:

输入: g = [1,2,3], s = [1,1]
输出: 1
解释: 
你有三个孩子和两块小饼干,3个孩子的胃口值分别是:1,2,3。
虽然你有两块小饼干,由于他们的尺寸都是1,你只能让胃口值是1的孩子满足。
所以你应该输出1。

示例 2:

输入: g = [1,2], s = [1,2,3]
输出: 2
解释: 
你有两个孩子和三块小饼干,2个孩子的胃口值分别是1,2。
你拥有的饼干数量和尺寸都足以让所有孩子满足。
所以你应该输出2.

首先我们思考贪心策略,本题由于饼干不能分开(一块饼干只能喂一个人) 所以如果想让更多的人吃到饼干 我们就要让小饼干喂饱小肚子 或者(大饼干喂大肚子)两个指针分别指向饼干和胃口即可

方法一:(小饼干填小肚子)

public int findContentChildren(int[] g, int[] s) {
        //s 是饼干序列 g 是胃口序列
       //首先肯定是要排序的,让饼干和胃口都从小到大!
        Arrays.sort(g);
        Arrays.sort(s);
        int p1=0;//饼干的指针
        int p2=0;//胃口的指针
        int res=0;//记录已分配的饼干数量
        while (p1<s.length&&p2<g.length){//只要饼干和胃口有一个走完就没必要再分配饼干
            if (s[p1]>=g[p2]){//小饼干大于小胃口  饼干和胃口同时往后走
                p1++;
                p2++;
                res++;
            }else {//饼干没有大于胃口 换下一个更大的饼干
                p1++;
            }
        }
        return res;
    }

方法二:(大饼干填大肚子)

 public int findContentChildren(int[] g, int[] s) {
        //s 是饼干序列 g 是胃口序列
       //首先肯定是要排序的,让饼干和胃口都从小到大!
        Arrays.sort(g);
        Arrays.sort(s);
        int p1=s.length-1;//饼干的指针
        int p2=g.length-1;//胃口的指针
        int res=0;//记录已分配的饼干数量
        while (p1>=0&&p2>=0){//只要饼干和胃口有一个走完就没必要再分配饼干
            if (s[p1]>=g[p2]){//小饼干大于小胃口  饼干和胃口同时往前走
                p1--;
                p2--;
                res++;
            }else {//饼干没有大于胃口 换下一个小肚子
                p2--;
            }
        }
        return res;
    }

哈哈 其实这道题算是贪心入门题 本质上并不难 双指针也很好理解  只要按需分配即可!

二、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] 。

这道题题意不难理解 ,甚至贪心策略也很好想(我们每次取反都取最小的元素)然后再for循环求所有元素累计和

why  为什么 每次把最小的元素取反 总的数组和可以取最大?

我们首先把数组中的元素分为两种情况:

1、数组中仍有负数  负数中最小的负数经过排序 每次必然处于第一个下标  我们把负数取反 变为正数  是不是总的数组和就大了!

2、数组中没有负数 但我们k还没取完  所以只能忍痛割爱 把最小的正数变成负数!这样的数组和就是最大的

public int largestSumAfterKNegations(int[] nums, int k) {
        while (k>0){//k次取反
            Arrays.sort(nums);//每次挑选最小的元素取反 
            nums[0]=-nums[0];
            k--;
        }
        int res=0;//求累加和
        for (int i=0;i<nums.length;i++){
            res+=nums[i];
        }
        return res;
        
    }

 上边的贪心策略虽然很好想,但......

k还未结束的时候 每次都要进行快排 时间复杂度达到惊人的 k*N*log^N

ok  我们换个思路来看这道题 还是刚才的两个方面来思考 

1、数组中仍有负数 :

我们把负数取反他肯定跑到后边去了,我们只要用一个下标来控制 取到“第二小”的数字取反即可 无需再进行一次排序

2、数组中没有负数:

我们只能“忍痛割爱”把最小的正数取反 但取反过后这个数字必然成了数组中的最小的数(因为原来的数组已经没负数了) 如果此时k没结束 我们只需要对这个最小的数左右横条 消耗完剩下k即可

思路如下 :我们先对数组进行排序  只需要一次for循环 如果数组中仍有负数 并且k还有剩余  就把这个负数取反 然后加到结果里, 如果没有负数 我们就直接把这个遍历到的这个正数 加到结果里

如果剩余的k是偶数 nums[0]    nums[0]是数组中最小的正数,进行左右横跳最后左右横条一定是正数(当前数组已经没负数) 如果剩余的k是奇数  nums【0】左右横跳一定是负数 我们就用res-2倍的nums【0】(因为我们当时加入了正数的nums[0])

上代码!

  public int largestSumAfterKNegations(int[] nums, int k) {
        Arrays.sort(nums);//先对数组进行排序
        int res=0;//结果
        for (int i=0;i<nums.length;i++){//for循环主要是为了实现数组和累加 顺便把负数变成正数
            if (nums[i]<0&&k>0){
                nums[i]=-nums[i];//负数取反
                k--;//消耗了一次取反
            }
            res+=nums[i];
        }
        Arrays.sort(nums);//为了找到全是正数的数组中最小的那个最小的正数  (k已经用光的情况 也不影响)
        return  res-(k%2==0?0:2*nums[0]);//消耗剩下的k 返回真正的结果
    }

当然 有可能数组中还有负数 我们的k用完了 但是没关系 我们只是多了一次排序 k==0 对最后返回的结果也没有影响!

  • 40
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 32
    评论
评论 32
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值