LeetCode刷题:贪心算法 [135.分发糖果] - Java版本

贪心算法:分配问题

只是记录自己的刷题过程,答案参考过多种题解。

如有错误感谢指正!

参考:LeetCode 101: A LeetCode Grinding Guide (C++ Version) 作者:高畅 Chang Gao

题目来源:题库 - 力扣 (LeetCode) 全球极客挚爱的技术成长平台 (leetcode-cn.com)

135.分发糖果 (Hard)

一群孩子站成一排,每一个孩子有自己的评分。现在需要给这些孩子发糖果,规则是如果一个孩子的评分比自己身旁的一个孩子要高,那么这个孩子就必须得到比身旁孩子更多的糖果;所有孩子至少要有一个糖果。求解最少需要多少个糖果。

A. 分发糖果的贪心思想

  • 这道题在每次遍历中,一定要只考虑并更新 相邻一侧 的大小关系,再确定另一边。例如比较每一个孩子的右边,然后再比较左边,如果两边一起考虑一定会顾此失彼

  • 局部最优体现在,①一次是从左到右遍历,只比较右边孩子评分比左边大的情况;②一次是从右往左遍历,只比较左边孩子评分比右边大的情况。③这样保证第 i 个小孩的糖果数量即大于左边的也大于右边的。

  • 全局最优体现在,①相邻的孩子中,评分高的右孩子获得比左边孩子更多的糖果;②相邻的孩子中,评分高的左孩子获得比右边孩子更多的糖果;③相邻的孩子中,评分高的孩子获得更多的糖果。

B. 具体实现

  • 新建存储每个孩子的糖果数量的数组,并初始化每个孩子拥有的糖果数为 1。

  • 按数组下标顺序遍历两次,第一次正序遍历,若右边孩子的评分比左边的高,则右边孩子的糖果数更新为左边孩子的糖果数加 1;

  • 第二次倒序遍历,若左边孩子的评分比右边的高,且左边孩子当前的糖果数 不大于 右边孩子的糖果数,则左边孩子的糖果数更新为右边孩子的糖果数加 1。max(第一次遍历的当前糖果数,右孩子糖果数+1)。

  • 仔细考虑两次遍历时,数组的起点下标,避免出现空指针异常。

  • 最后对所有的糖果数量求和。

C. 思考

  • 评分和糖果数是两个独立的变量,但数组下标变量和前两者有联系。

  • 对于一组孩子对应的糖果数来说,除了首位两个数只有单侧存在相邻数,其余中间数的双侧都存在相邻数。两两比较 时,先考虑一侧的大小情况(例如先考虑右),等这一侧(右)全部判断完后,在再考虑另一侧(左)。

  • 为什么第一次判断右孩子比左孩子大时,采取了正序遍历,而不是倒序遍历?因为第一组【左值A,右值B】的右值B增大后,第二组【第一组右值B,新右值C】在判断时,就可以在B的基础上再增大,不会出现遗漏。如果从右往左(倒叙)判断右孩子比左孩子大,那么每次都不能利用上 前一次的比较结果 了。(第二次遍历同理)

  • 用 for() 循环主要是每个数组下标对应的数都要用到,遍历完后自动停止,所以不需要通过 while() 来自己设定结束条件。

  • 上一题即知道 孩子需求度 又知道 饼干大小 ,相当于告知了饼干具体数量(已知主要条件),只需用现有的饼干进行比较判断(需求 <= 饼干大小 ? )并匹配给孩子,就可求出满足的孩子数量的最大值。(孩子和饼干的数量和属性都知道,找出之间关系)

  • 这一题只知道 孩子评分 ,而糖果没有具体数量(未知主要条件),只给了其他次要条件,现在就让你来求这个饼干的具体数量。(孩子知道数量和属性,糖果知道属性不知道数量,也知道两者间的关系,求糖果的数量)​

D. 代码实现

public int candy(int[] ratings) {
    int[] suger = new int[ratings.length];
    suger[0] = 1;
    // 如果孩子没有或只有一位,则不需要进行分配,直接返回孩子个数即可。
    if (ratings.length < 2) {
        return ratings.length;
    }
    // 顺序遍历
    for (int i = 1; i < ratings.length; i++) {
        if ( ratings[i] > ratings[i - 1]) { //右大于左
            suger[i] = suger[i - 1] + 1;
        } else {
            suger[i] = 1; //初始化其他为1
        }
    }
    // 倒序遍历
    for (int i = ratings.length - 2; i >= 0; i--) {
        if (ratings[i] > ratings[i + 1]) { //左大于右
            suger[i] = Math.max(suger[i], suger[i + 1] + 1); //取【左孩子本身,(右孩子+1)】的糖果最大值
        }
    }
    int count = 0;
    for (int i : suger) {
        count += i;
    }
    return count;
}

猜你喜欢

转载自blog.csdn.net/weixin_51909882/article/details/122139597