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
解法一:
class Solution {
public:
// 主函数,用于计算最少的糖果数量
int candy(vector<int>& ratings) {
// 获取评级的数量
int n = ratings.size();
// 创建一个左边数组,用于存储每个孩子从左边开始的连续递增序列的长度
vector<int> left(n);
// 从左到右遍历评级
for (int i = 0; i < n; i++) {
// 如果当前孩子评级大于前一个孩子的评级,那么他的糖果数是前一个孩子的糖果数加一
if (i > 0 && ratings[i] > ratings[i - 1])
left[i] = left[i - 1] + 1;
// 如果当前孩子评级不大于前一个孩子的评级,那么他至少得到一个糖果
else
left[i] = 1;
}
// right用于存储从右到左当前连续递增序列的长度
int right = 0, ret = 0;
// 从右到左遍历评级
for (int i = n - 1; i >= 0; i--) {
// 如果当前孩子评级大于后一个孩子的评级,那么他的糖果数是right的值加一
if (i < n - 1 && ratings[i] > ratings[i + 1])
right++;
// 如果当前孩子评级不大于后一个孩子的评级,那么他至少得到一个糖果
else
right = 1;
// 最少糖果数量是left和right中的最大值,这样可以保证同时满足左边和右边的需求
ret += max(right, left[i]);
}
// 返回最少的糖果数量
return ret;
}
};
首先,用一个数组left来记录每个孩子从左边开始的连续递增序列的长度。对于每个孩子,如果他的评级大于前一个孩子的评级,那么他的糖果数是前一个孩子的糖果数加一;否则,他至少得到一个糖果。这样遍历完成后,left数组中存储的是每个孩子最少需要的糖果数量。
然后,用一个变量right来记录从右到左当前连续递增序列的长度。对于每个孩子,如果他的评级大于后一个孩子的评级,那么他的糖果数是right的值加一;否则,他至少得到一个糖果。这样遍历完成后,right的值就是最右边的孩子最少需要的糖果数量。
最后,再次遍历评级,最少糖果数量是left和right中的最大值,这样可以保证同时满足左边和右边的需求。将所有孩子的最少糖果数量相加即是最终的结果,最后返回即可。
解法二:
class Solution {
public:
// 主函数,计算最少的糖果数量
int candy(vector<int>& ratings) {
// 获取孩子的数量
int n = ratings.size();
// 初始化糖果总数为1
int ret = 1;
// inc 记录递增序列的长度,dec 记录递减序列的长度,pre 记录前一个孩子得到的糖果数量
int inc = 1, dec = 0, pre = 1;
// 从第二个孩子开始遍历
for (int i = 1; i < n; i++) {
// 如果当前孩子的评分比前一个高
if (ratings[i] >= ratings[i - 1]) {
// 结束递减序列
dec = 0;
// 如果当前评分与前一个相同,则当前孩子得到的糖果数量至少比前一个多1,否则根据前一个孩子的糖果数量+1
pre = ratings[i] == ratings[i - 1] ? 1 : pre + 1;
// 将当前孩子得到的糖果数量加到总数中
ret += pre;
// 更新递增序列的长度
inc = pre;
} else {
// 如果当前孩子的评分比前一个低,开始递减序列
dec++;
// 如果递减序列的长度与递增序列相同,增加递减序列的长度
if (dec == inc) {
dec++;
}
// 将递减序列的糖果数量加到总数中
ret += dec;
// 重置前一个孩子的糖果数量为1,因为新序列开始了
pre = 1;
}
}
// 返回最少需要的糖果总数
return ret;
}
};
上述代码是按照从左到右遍历每个孩子评级的思路进行编写的。
首先,我们初始化总糖果数为1,并设置变量inc、dec和pre的初始值为1。变量inc记录递增序列的长度,dec记录递减序列的长度,pre记录前一个孩子得到的糖果数量。
然后,从第二个孩子开始遍历。对于每个孩子,如果他的评级大于等于前一个孩子的评级,说明当前是递增序列的一部分。我们结束递减序列,将dec重新设置为0,并根据当前评级与前一个评级是否相同来设置当前孩子得到的糖果数量。如果相同,则当前孩子得到的糖果数量至少比前一个多1;如果不同,则根据前一个孩子的糖果数量+1设置当前孩子的糖果数量。然后,将当前孩子得到的糖果数量加到总数ret中,并更新inc的值为pre,表示当前递增序列的长度。
如果当前孩子的评级比前一个孩子低,说明进入了递减序列。我们将递减序列的长度dec加1,并检查递减序列的长度是否与递增序列的长度相同。如果相同,表示递减序列的长度需要增加1,因为递减序列和递增序列不能相同长度。然后,将递减序列的糖果数量加到总数ret中,并将pre重新设置为1,表示新序列开始了。
最后,返回总糖果数ret即可。
这种方法只需要遍历一次孩子评级,时间复杂度为O(n),空间复杂度为O(1)。
这里一定要注意当inc==dec时,dec还要加1,比如2 2 1,这里进行到下标2时候,inc=1,dec=1,如果不进行上述操作,那么最终结果就是3,所以一定要记得dec再+1.因为这个代码只是从左往右对比,没有再从右往左比一次,所以这个操作是不能忽略的。