题目描述
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
方法一(两次遍历)
解题思路
规则定义: 设学生 A 和学生 B 左右相邻,A 在 B 左边;
左规则: 当 ratingsB>ratingsA时,B 的糖比 A 的糖数量多。
右规则: 当 ratingsA>ratingsB 时,A的糖比 B 的糖数量多。
相邻的学生中,评分高的学生必须获得更多的糖果 等价于 所有学生满足左规则且满足右规则。
算法流程
1.先从左至右遍历学生成绩 ratings,按照以下规则给糖,并记录在 left 中:
先给所有学生 1颗糖;
- 若 ratingsi>ratings[i−1] ,则第 i名学生糖比第 i−1名学生多 1 个。
- 若 ratingsi<=ratings[i−1],则第 i名学生糖数量不变。(交由从右向左遍历时处理。)
经过此规则分配后,可以保证所有学生糖数量 满足左规则 。
2.同理,在此规则下从右至左遍历学生成绩并记录在 right 中,可以保证所有学生糖数量 满足右规则 。
3.最终,取以上 2 轮遍历 left 和 right 对应学生糖果数的 最大值 ,这样则 同时满足左规则和右规则 ,即得到每个同学的最少糖果数量。
class Solution {
public int candy(int[] ratings) {
int[] left = new int[ratings.length];
int[] right = new int[ratings.length];
Arrays.fill(left, 1);
Arrays.fill(right, 1);
for(int i = 1; i < ratings.length; i++)
if(ratings[i] > ratings[i - 1]) left[i] = left[i - 1] + 1;
int count = left[ratings.length - 1];
for(int i = ratings.length - 2; i >= 0; i--) {
if(ratings[i] > ratings[i + 1]) right[i] = right[i + 1] + 1;
count += Math.max(left[i], right[i]);
}
return count;
}
}
方法二(单次遍历)
大原则呢,就是:
- 上升序列,越高的,发的越多。[以1递增]
- 下降序列,下降序列每多一个人,就给序列中每个人都发一颗糖,以维持公平
- 此外,如果下降长度超过了上升序列,要把下降序列前一个元素也囊入下降序列。
- 即峰值元素也要按照下降序列规则多发糖了不然中间分数最高那个小朋友,仅靠递增序列拿的糖,不能维持右侧下降序列的公平了
- 然后就是分数相同的情况
- 如果这个小孩跟前面分数一样,欺负他就完事了,先给他一个糊弄一下,如果后面是递减,补给他就是了。递增就不用搭理他了
/**
* 执行用时:2 ms , 在所有 Java 提交中击败了 99.87% 的用户
* 内存消耗: 39.6 MB , 在所有 Java 提交中击败了 43.15% 的用户
* */
public int candy(int[] ratings) {
// count 为当前所需糖果数
// upcount/downcount 分别为上升序列长度,下降序列长度
int count = 1, upcount = 1, downcount = 0;
for (int i = 1; i < ratings.length; i++) {
if (ratings[i-1] < ratings[i]){ // 如果当前为上升序列
if(downcount != 0){ // 根据下降序列长度判断他所处位置
upcount = 1;
}
downcount = 0;
upcount++;
count += upcount;
}else if(ratings[i-1] == ratings[i]){ // 如果分数相同,则可以欺负这小孩儿
downcount = 0;
upcount = 1;
count ++;
}else { // 下降序列
downcount++;
if(downcount == upcount){ // 这里要特别注意。如果下降序列要比上升序列长(具体理解见下图)
downcount++; // 那么往后的计数,要把峰值也算进下降序列中了,
} // 否则下降序列首位同学要比他隔壁上升序列队尾那个孩子拿的多了……
count += downcount;
}
}
return count;
}