代码随想录算法训练营第三十一天-贪心算法1|455.分发饼干 376. 摆动序列 53. 最大子序和

说实话贪心算法并没有固定的套路

所以唯一的难点就是如何通过局部最优,推出整体最优

有同学问了如何验证可不可以用贪心算法呢?

最好用的策略就是举反例,如果想不到反例,那么就试一试贪心吧

一般数学证明有如下两种方法:

  • 数学归纳法
  • 反证法

面试中基本不会让面试者现场证明贪心的合理性,代码写出来跑过测试用例即可,或者自己能自圆其说理由就行了

贪心算法一般分为如下四步:

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







2.解题思路 

  • 为了满足更多的小孩,就不要造成饼干尺寸的浪费。
  • 大尺寸的饼干既可以满足胃口大的孩子也可以满足胃口小的孩子,那么就应该优先满足胃口大的。
  • 这里的局部最优就是大饼干喂给胃口大的,充分利用饼干尺寸喂饱一个,全局最优就是喂饱尽可能多的小孩

 

 

首先对数组 gg 和 ss 排序,然后从小到大遍历 gg 中的每个元素,对于每个元素找到能满足该元素的 ss 中的最小的元素。具体而言,令 ii 是 gg 的下标,jj 是 ss 的下标,初始时 ii 和 jj 都为 00,进行如下操作。

对于每个元素 g[i]g[i],找到未被使用的最小的 jj 使得 g[i] \le s[j]g[i]≤s[j],则 s[j]s[j] 可以满足 g[i]g[i]。由于 gg 和 ss 已经排好序,因此整个过程只需要对数组 gg 和 ss 各遍历一次。当两个数组之一遍历结束时,说明所有的孩子都被分配到了饼干,或者所有的饼干都已经被分配或被尝试分配(可能有些饼干无法分配给任何孩子),此时被分配到饼干的孩子数量即为可以满足的最多数量。

import java.util.Arrays;

//胃口值 g 饼干s
public class AssignCookies {

    // 思路1:优先考虑饼干,小饼干先喂饱小胃口
    public int findContentChildren(int[] g, int[] s) {
        Arrays.sort(g);
        Arrays.sort(s);
        int m = g.length;
        int n = s.length;
        int count = 0;
        for (int i = 0, j = 0; i < m && j < n; i++, j++){
            while (j < n && g[i] > s[j]){
                j++;
            }if(j < n){
                count++;
            }
        }return count;


    }
}

// 思路2:优先考虑胃口,先喂饱大胃口
public int findContentChildren(int[] g, int[] s) {
    Arrays.sort(g);
    Arrays.sort(s);
    int count = 0;
    int start = s.length - 1;
    // 遍历胃口
    for (int index = g.length - 1; index >=0; index--){
        if(start >=0 && g[index] <= s[start]){
            start--;
            count++;
        }
    }
    return count;

 376. 摆动序列

public class WiggleSubsequence {
    public int wiggleMaxLength(int[] nums) {
        if(nums.length == 1){
            return 1;
        }
        int res = 1;
        int cur = 0;//当前一对差值
        int pre = 0;// 前一对差值
        for(int i = 1; i < nums.length; i++){
            cur = nums[i] - nums[i - 1];
            // 出现峰值
            if(cur > 0 && pre <=0 || cur < 0 && pre >=0){
                res++;
                pre = cur;

            }
        }
        return res;

    }
}

53. 最大子序和

贪心贪的是哪里呢?

如果 -2 1 在一起,计算起点的时候,一定是从1开始计算,因为负数只会拉低总和,这就是贪心贪的地方!

局部最优:当前“连续和”为负数的时候立刻放弃,从下一个元素重新计算“连续和”,因为负数加上下一个元素 “连续和”只会越来越小。

全局最优:选取最大“连续和”

局部最优的情况下,并记录最大的“连续和”,可以推出全局最优

从代码角度上来讲:遍历nums,从头开始用count累积,如果count一旦加上nums[i]变为负数,那么就应该从nums[i+1]开始从0累积count了,因为已经变为负数的count,只会拖累总和。

这相当于是暴力解法中的不断调整最大子序和区间的起始位置

那有同学问了,区间终止位置不用调整么? 如何才能得到最大“连续和”呢?

区间的终止位置,其实就是如果count取到最大值了,及时记录下来了。例如如下代码:

if (count > result) result = count;

 

public class MaximumSubarray {
    public int maxSubArray(int[] nums) {
        int res = Integer.MIN_VALUE;//结果
        int count = 0;//根据count值来更新结果
        for(int i = 0; i < nums.length; i++){
            count += nums[i];//count每次累加num[i]的值
            //count比res小,更新结果
            if(count > res){
                res = count;
                //count小于0,则将count归零,并从i+1开始累加
            }if(count < 0){
                count = 0;
            }
        }
        return res;

    }
}

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值