Hard
题目描述
有 n 个孩子站成一排。每个孩子都有一个评分,用整数数组 ratings 表示。
你需要按照以下要求,给这些孩子分发糖果:
- 每个孩子至少分得一颗糖果。
- 评分更高的孩子必须比他两侧评分低的邻居分得更多的糖果。
请你给出能够满足上述要求的最少糖果数目。
示例 1:
输入:ratings = [1,0,2]
输出:5
解释:你可以分别给第一个、第二个、第三个孩子分发 2、1、2 颗糖果。
示例 2:
输入:ratings = [1,2,2]
输出:4
解释:你可以分别给第一个、第二个、第三个孩子分发 1、2、1 颗糖果。
第三个孩子只得到 1 颗糖果,这满足题面上的两个条件。
提示:
n == ratings.length1 <= n <= 2 * 10^40 <= ratings[i] <= 2 * 10^4
解题思路
这道题是经典的贪心算法问题。关键在于理解约束条件:评分高的孩子要比相邻评分低的孩子得到更多糖果。
解法一:两次遍历(推荐) 我们可以将问题分解为两个单向约束:
- 从左到右遍历:如果右边孩子评分更高,糖果数比左边多1
- 从右到左遍历:如果左边孩子评分更高,糖果数应该比右边多1
具体步骤:
- 初始化所有孩子糖果数为1
- 从左到右遍历,如果
ratings[i] > ratings[i-1],则candies[i] = candies[i-1] + 1 - 从右到左遍历,如果
ratings[i] > ratings[i+1]且candies[i] <= candies[i+1],则candies[i] = candies[i+1] + 1
解法二:一次遍历 + 峰谷分析 通过识别递增、递减序列来计算糖果分配,但实现较复杂。
两次遍历法思路清晰,易于理解和实现,是最优解法。
代码实现
class Solution {
public:
int candy(vector<int>& ratings) {
int n = ratings.size();
vector<int> candies(n, 1);
// 从左到右遍历
for (int i = 1; i < n; i++) {
if (ratings[i] > ratings[i - 1]) {
candies[i] = candies[i - 1] + 1;
}
}
// 从右到左遍历
for (int i = n - 2; i >= 0; i--) {
if (ratings[i] > ratings[i + 1]) {
candies[i] = max(candies[i], candies[i + 1] + 1);
}
}
return accumulate(candies.begin(), candies.end(), 0);
}
};
class Solution:
def candy(self, ratings: List[int]) -> int:
n = len(ratings)
candies = [1] * n
# 从左到右遍历
for i in range(1, n):
if ratings[i] > ratings[i - 1]:
candies[i] = candies[i - 1] + 1
# 从右到左遍历
for i in range(n - 2, -1, -1):
if ratings[i] > ratings[i + 1]:
candies[i] = max(candies[i], candies[i + 1] + 1)
return sum(candies)
public class Solution {
public int Candy(int[] ratings) {
int n = ratings.Length;
int[] candies = new int[n];
Array.Fill(candies, 1);
// 从左到右遍历
for (int i = 1; i < n; i++) {
if (ratings[i] > ratings[i - 1]) {
candies[i] = candies[i - 1] + 1;
}
}
// 从右到左遍历
for (int i = n - 2; i >= 0; i--) {
if (ratings[i] > ratings[i + 1]) {
candies[i] = Math.Max(candies[i], candies[i + 1] + 1);
}
}
return candies.Sum();
}
}
var candy = function(ratings) {
const n = ratings.length;
const candies = new Array(n).fill(1);
// 从左到右遍历
for (let i = 1; i < n; i++) {
if (ratings[i] > ratings[i - 1]) {
candies[i] = candies[i - 1] + 1;
}
}
// 从右到左遍历
for (let i = n - 2; i >= 0; i--) {
if (ratings[i] > ratings[i + 1]) {
candies[i] = Math.max(candies[i], candies[i + 1] + 1);
}
}
return candies.reduce((sum, candy) => sum + candy, 0);
};
复杂度分析
| 复杂度类型 | 时间复杂度 | 空间复杂度 |
|---|---|---|
| 两次遍历法 | O(n) | O(n) |