题目
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
题目解析
由题目可知ratings[i]>ratings[i-1]时,第i个孩子要比第i-1个孩子多1个糖果;
当ratings[i]<ratings[i-1]时,第i个孩子要比第i-1个孩子少一个糖果。
因此想到使用贪心算法,从左到右遍历一遍数组,找到从左到右递增的;再从右到左遍历一遍数组,找到从右到左递增的。两个数组同一个下标取最大值,即可得到结果。(算法一)
解法一
class Solution {
public int candy(int[] ratings) {
int result=0;
int[] left = new int[ratings.length];
// 从左向右开始计算
left[0]=1;
for(int i=1;i<ratings.length;i++){
if (ratings[i-1]<ratings[i]) {
left[i]=left[i-1] + 1;
}else{
left[i] =1;
}
}
int[] right = new int[ratings.length];
right[ratings.length-1] = 1;
for(int i=ratings.length-1;i>0;i--){
if (ratings[i-1]>ratings[i]) {
right[i-1]=right[i] + 1;
}else{
right[i-1] =1;
}
}
for(int i=0;i<ratings.length;i++){
result+=Math.max(left[i], right[i]);
}
return result;
}
}
由于遍历了3遍ratings,因此时间复杂度是O(3N),常数忽略不计,复杂度为O(N)。
空间复杂度使用了left,right和result,空间复杂度也为O(N)。
其实result完全是多余的,只要从右往左计算的时候,直接取最大即可,优化后依然为O(N)。
解法二
还有一种思路,从左向右,要么是非严格递增,要么是递减,当递增的时候,第i+1个孩子要比第i个多一个糖果,递减的时候分配给当前同学一个糖果,并把该同学所在的递减序列中所有的同学都再多分配一个糖果即可满足要求。因此获得如下:
lass Solution {
public int candy(int[] ratings) {
int n = ratings.length; //孩子总数
int ret = 1; //因为从第二个孩子开始计算,默认第一个孩子已经有了一个糖果。
int inc = 1; //递增序列长度
int dec = 0; //递减序列长度
int pre = 1; //第一个孩子已经有了一个糖果,因此前一个孩子拥有的数量为1
for (int i = 1; i < n; i++) {
if (ratings[i] >= ratings[i - 1]) { //递增
dec = 0; //递增序列重置
pre = ratings[i] == ratings[i - 1] ? 1 : pre + 1; //相同则不算递增,重置为1;递增则前一个孩子的糖果数+1
ret += pre;
inc = pre;
} else {
dec++;
if (dec == inc) { //当递减序列长度等于递增序列长度时,需要额外给递减序列的第一个孩子增加 1 颗糖果,以满足条件。
dec++;
}
ret += dec;
pre = 1;
}
}
return ret;
}
}
对ratings只循环了一次,时间复杂度仍为O(N)。
但此方法只使用了4个变量来记录变化,因此空间复杂度为O(1)。
题目思考
主要采用贪心算法,基于局部最优解来构建全局最优解。基于有序条件(如评分大小)的贪心策略在许多涉及选择、分配、优化的问题中都有应用,关键在于找到合适的衡量标准(类似评分)来指导贪心选择的过程。