题目链接
https://leetcode.com/problems/candy/
题目描述
有n个孩子站成一条直线,老师根据每个孩子的表现预先给他们打分并分发糖果。分发糖果的规则为:
(1)每个孩子至少分到1个糖果。
(2)评分更高的孩子必须比他两侧的邻位孩子获得更多的糖果。
请问老师至少需要准备多少颗糖果?
示例
输入:ratings =[1,0,2]
输出:5
分别给三个孩子分发2、1、2颗糖果。
输入:ratings = [1,2,2]
输出:4
分别给三个孩子分发1、2、1颗糖果。第三个孩子得到1颗糖果也是符合题意的。
解题思路
这个问题涉及到数组中当前值和左右两边值的大小比较 ,比较结果会影响当前孩子得到的糖果数量。这种涉及到两处比较的情况,我们一般要先比较一侧,再比较另一侧,在两个方向上进行遍历。开数组candy,candy[i]表示索引为i的小孩得到的糖果。
首先我们先从左往右遍历,保证在右侧且分数更高的孩子能够比左侧的孩子得到更多的糖果。此时就用到“贪心思想“,局部最优策略为:”只要右边孩子的评分比左边孩子大,右边的孩子就多一个糖果“,这样能够达成”全局最优“:相邻的孩子中,评分更高的右边孩子获得比左边孩子更多的糖果。在实现时,由于每个孩子至少有一颗糖,初始化先给每个孩子一颗糖。如果ratings(i)>ratings(i-1),那么candy[i] = candy[i-1] + 1。如果ratings(i)<=ratings(i-1),不进行任何操作(留给下一轮遍历时处理),candy[i]仍为1。
然后从右往左遍历,保证在左侧且分数更高的孩子能够比右侧的孩子得到更多的糖果(反过来进行贪心)。我们直接在candy数组上进行修改,如果ratings(i)>ratings(i+1)(左边孩子的评分比右边孩子的评分大),此时candy[i]的取值有两种选择,一个是candy[i+1] + 1(比右边的孩子多得到一个糖果),一个是维持从左往右遍历时得到的candy[i]结果(上一轮只考虑右边孩子比左边孩子高时得到的糖果数量,可能为1,也可能为candy[i-1]+1)。此时又要用到“贪心思想“,由于分数更高的孩子要比两侧的邻位孩子得到更多的糖果,因此candy[i]的局部最优取值为max(candy[i+1]+1,candy[i]),这样能够保证如果位于索引i的孩子比左右相邻的孩子评分都高,candy[i]的值既大于candy[i-1],又大于candy[i+1]。
两次遍历完成后达到全局最优:相邻的孩子中评分高的孩子获得更多的糖果。
Python实现
class Solution:
def candy(self, ratings: List[int]) -> int:
candy = [1 for _ in range(len(ratings))] #初始化每个孩子有一个糖果
for i in range(1,len(ratings)):#首先从左往右遍历,处理右边孩子比左边孩子评分高的情况
if (ratings[i] > ratings[i-1]):
candy[i] = candy[i-1] + 1
for i in range(len(ratings)-2,-1,-1):
if (ratings[i]>ratings[i+1]): #从右往左遍历,处理左边孩子比右边孩子评分高的情况
candy[i] = max(candy[i+1]+1,candy[i])
return sum(candy)