135. 分发糖果

题目

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 * 10^4
  • 0 <= ratings[i] <= 2 * 10^4

 

贪心方法

这个问题其难点就在于贪心的策略,如果在考虑局部的时候想两边兼顾,就会顾此失彼。

那么本题我们采用了两次贪心的策略

  • 一次是从左到右遍历(第一个元素左边没有元素,天然满足,所以可以从第二个元素起,从左到右进行局部贪心遍历),只比较右边孩子评分比左边大的情况。
  • 一次是从右到左遍历(第n个元素右边没有元素,天然满足,所以可以从第n-1个元素起,从右到左进行局部贪心遍历),只比较左边孩子评分比右边大的情况。

这样的贪心策略,可以从局部最优推出全局最优,即:相邻的孩子中,评分高的孩子获得更多的糖果。

单向遍历时,从何处起始,是使得局部最优可以退出全局最优的关键,初始处必须天然的满足一侧的限制要求,使得我们可以专注的仅关注单侧限制,否则起始处就两种限制混杂,通过左右和右左两个局部贪心是得不出全局最优的。

class Solution {
    public int candy(int[] ratings) {

        int n = ratings.length;
        int[] temp = new int[n];
        temp[0] = 1;

        // 从前向后:确保右边评分大于左边时,满足放置条件
        for(int i = 1; i < n; i++) 
        {
            if (ratings[i] > ratings[i - 1]) 
            {
                temp[i] = temp[i - 1] + 1;
            } 
            else 
            {
                temp[i] = 1;
            }
        }

        // 从后向前:确保左边评分大于右边时,满足放置条件
        for(int i = n - 2; i >= 0; i--) 
        {
            if(ratings[i] > ratings[i + 1]) 
            {
                // temp[i]只有取最大的才能同时满足左右两种情形下的放置条件
                // 想不通可以模拟下例子:[1,3,4,5,2]
                temp[i] = Math.max(temp[i], temp[i + 1] + 1);
            }
        }

        // 统计结果
        int ret = 0;
        for (var cnt : temp) 
        {
            ret += cnt;
        }
        return ret;
    }
}

  

这道题如果假设孩子们首尾排成一个环,其他要求不变,则我们可以找出ratings数组中的最小值,将这个位置放置1,然后抠除这个位置的孩子,将环断开,然后按照上面的思路进行,最最后再加上最小值处放置的1即可(我们可以这样断开环,是因为这个位置评分最小,贪心时只需要放置1,无论其他位置放置何元素,天然满足关系)。

其实,上面不是环的情况,也是在首尾元素之间通过添加一个小于ratings数组所有元素的假想孩子连成一个环的特殊情况,规定这个假想孩子处必须放置0

posted on 2022-06-15 22:56  朴素贝叶斯  阅读(52)  评论(0编辑  收藏  举报

导航