0
点赞
收藏
分享

微信扫一扫

什么是VR虚拟社区|VR元宇宙平台|VR主题馆加盟

7dcac6528821 03-03 17:30 阅读 2

语言:Java/Go

理论

贪心的本质是选择每一阶段的局部最优,从而达到全局最优

什么时候用贪心?可以用局部最优退出全局最优,并且想不到反例到情况

贪心的一般解题步骤

  • 将问题分解为若干个子问题
  • 找出适合的贪心策略
  • 求解每一个子问题的最优解
  • 将局部最优解堆叠成全局最优解

455.分发饼干

本题的目标是满足更多孩子的胃口,因此大的饼干就优先满足胃口大的孩子,才会确保没有资源浪费。因此局部最优解就是将一定尺寸的饼干尽量喂给与这个尺寸接近胃口的孩子。全局最优就是喂饱尽可能多的孩子。可以先将饼干和胃口数组进行排序,从后向前遍历孩子数组,将大饼干给胃口大的孩子,并且设置一个count统计满足小孩的数量。

class Solution {
    public int findContentChildren(int[] g, int[] s) {
        //先对两个数组进行排序
        Arrays.sort(g);
        Arrays.sort(s);
        int count=0;
        int index=s.length-1;  //饼干数组的下标
        for(int i=g.length-1;i>=0;i--){//先遍历胃口
            if(index>=0 && g[i]<=s[index]){ //再去查找饼干
                count++;
                index--;  //满足条件才会把饼干分配出去,饼干的指针也往前移动
            }
        }
        return count;
    }
}

⚠️注意:如果是从后向前遍历,应该先遍历胃口再遍历饼干,因为这样才不会出现饼干没有合理分配的情况。如果是用小饼干喂饱小胃口,则需要先遍历饼干,再遍历胃口。

376. 摆动序列

本题目的是通过删除某些元素,找到最长的摆动子序列并返回其长度。摆动序列如果在一个坐标轴上描点,应该是持续存在n个峰值的序列。如下图:

可以看出需要删除的节点应该是,单调递增/递减时,非端点的节点。此时一个坡度上会有两个局部峰值。全局最优就是找到局部峰值最多的序列。因为本题返回最长子序列的长度,所以不需要进行删除操作,只需要统计局部峰值的个数即可。

计算当前节点和前后节点的关系:prediff(nums[i] - nums[i-1])和curdiff(nums[i+1] - nums[i])

  • prediff < 0 && curdiff > 0 或者 prediff > 0 && curdiff < 0,满足这两个条件的时候,说明nums[i-1],nums[i],nums[i+1]均为峰值。
  • 若遇到平坡,假设上坡后平坡:即prediff > 0 && curdiff = 0,遇到最后一个平坡节点时候,应该是这样的条件:prediff = 0 && curdiff < 0,将这个节点保留,其他平坡的节点都删除。所以当前峰值的条件为(preDiff <= 0 && curDiff > 0) || (preDiff >= 0 && curDiff < 0)第一个情况就是下坡,第二个情况为上坡。

  • 若遇到单调坡度有平坡,我们只需要在 这个坡度 摆动变化的时候,更新 prediff 就行,这样 prediff 在 单调区间有平坡的时候 就不会发生变化,造成误判。

数组首尾两端:题目中说了,如果只有两个不同的元素,那摆动序列也是 2。例如序列[2,5],如果靠统计差值来计算峰值个数就需要考虑数组最左面和最右面的特殊情况。因为我们在计算 prediff(nums[i] - nums[i-1]) 和 curdiff(nums[i+1] - nums[i])的时候,至少需要三个数字才能计算,而数组只有两个数字。这里我们可以写死,就是 如果只有两个元素,且元素不同,那么结果为 2。也可以不写死,假设数组最前面还有一个数字,那这个数字应该是什么呢?之前我们在 讨论 情况一:相同数字连续 的时候, prediff = 0 ,curdiff < 0 或者 >0 也记为波谷。那么为了规则统一,针对序列[2,5],可以假设为[2,2,5],这样它就有坡度了即 preDiff = 0

class Solution {
    public int wiggleMaxLength(int[] nums) {
        if(nums.length<=1) return nums.length;
        int curDiff=0;  //记录当前节点和后一个节点的差值
        int preDiff=0; //记录前一个节点和当前节点的差值
        int result=1;
        for(int i=0;i<nums.length-1;i++){
            curDiff=nums[i+1]-nums[i];
            if(preDiff<=0&&curDiff>0 || preDiff>=0 && curDiff<0){//出现峰谷的时候
                    result++;
                    preDiff=curDiff;
            }
        }
        return result;
    }
}

53. 最大子序和

本题跟上题的区别在于,要求连续。因此不可以进行删除也不可以对数组重排序。本题的直接思路就是两层for循环嵌套,但是时间复杂度太高。可以考虑用贪心算法或动态规划算法。

先尝试找局部最优:比如看前两个元素:-2,1,肯定会先以1作为开头,把1加入到结果中,在sum上加上1,任何负值只会减少和的值。当以1为开头时,又遇到了-3,这时sum变成了负值,所以将1从结果中删除,从3点后面一个元素开始判断,以此类推。还要设置变量maxSum记录最大的和。因此,如果当加上nums[i]时sum变为负数时,就将sum变为0重新计算。

class Solution {
    public int maxSubArray(int[] nums) {
        if(nums.length==1) return nums[0];
        int sum=0;
        int maxSum=Integer.MIN_VALUE;
        for(int i=0;i<nums.length;i++){
          sum+=nums[i];
          maxSum=Math.max(sum, maxSum);
          if(sum<=0) sum=0;
        }
        return maxSum;
    }
}

举报

相关推荐

0 条评论