力扣 135. 分发糖果

发布时间 2023-04-21 00:11:02作者: 付玬熙

n 个孩子站成一排。给你一个整数数组 ratings 表示每个孩子的评分。

你需要按照以下要求,给这些孩子分发糖果:

  • 每个孩子至少分配到 1 个糖果。
  • 相邻两个孩子评分更高的孩子会获得更多的糖果。

请你给每个孩子分发糖果,计算并返回需要准备的 最少糖果数目 。

示例 1:

输入:ratings = [1,0,2]
输出:5
解释:你可以分别给第一个、第二个、第三个孩子分发 2、1、2 颗糖果。

示例 2:

输入:ratings = [1,2,2]
输出:4
解释:你可以分别给第一个、第二个、第三个孩子分发 1、2、1 颗糖果。
     第三个孩子只得到 1 颗糖果,这满足题面中的两个条件。

提示:

  • n == ratings.length
  • 1 <= n <= 2 * 104
  • 0 <= ratings[i] <= 2 * 104

题解

贪心,来自:https://www.youtube.com/watch?v=PzBYQA6FshA

因为要分发得最少,所以应该从前往后判断+从后往前,考虑两个方向。

第一次从前往后,使用数组left记录从前往后判断得到的分配结果,如1,2,6,5,4,3,2

因为最低分配一个,所以默认每一个人一个,left全1,接下来遍历比较当前元素和前一个元素:

  • 当前元素>前一个元素,则当前元素应该比前一个得到更多的糖,则更新分配为前一个+1left[i]=left[i-1]+1
  • 否则,分配1;

此时得到Left=1,2,3,1,1,1,可以发现没有从后往前的遍历,分配并不完全正确。

接下来从后往前,此时可以节约数组,只用一个变量right记录当前分配最大值,遍历比较当前元素和后一个元素:

  • 当前元素>后一个元素,则当前元素应该比后一个得到更多的糖,则更新分配为前一个+1right++,需要判断rightleft对应的分配,选取较大值;
  • 否则,right重置为1;

sum可以在从后往前遍历中进行累计。

查看代码
 class Solution {
public:
    int candy(vector<int>& ratings) {
        int n=ratings.size();
        vector<int>left(n,1);//从左往右
        //从左往右,比较当前i和前一个i-1
        for(int i=1;i<n;++i){
            left[i]=ratings[i]>ratings[i-1]?left[i-1]+1:1;
        }
        //从右往左,为了节约right数组,用变量right记录最大的值,边跑边比较,并且结合第一遍的结果选最大的,直接修改在left中
        int right=1;
        int sum=0;
        for(int j=n-2;j>=0;--j){
            //比较当前j和后一个j+1
            if(ratings[j]>ratings[j+1]){
                ++right;//累计
                //选最大的
                left[j]=left[j]>right?left[j]:right;
            }
            else
                right=1;

            sum+=left[j];//累计
        }
        sum+=left[n-1];//最后一个小朋友分配的还没有加
        return sum;
    }
};