题目描述
n 个孩子站成一排,每个孩子的评分为 ratings[i]。要求分发糖果满足:
- 每个孩子至少 1 颗糖果。
- 相邻孩子中评分更高的孩子必须获得更多糖果。
求最少需要准备的糖果总数。
示例 1:
输入:ratings = [1,0,2]
输出:5
解释:
- 孩子0:2颗(比右侧评分高)
- 孩子1:1颗(最低)
- 孩子2:2颗(比右侧评分高)
示例 2:
输入:ratings = [1,2,2]
输出:4
解释:
- 孩子0:1颗
- 孩子1:2颗(比左侧高)
- 孩子2:1颗(与右侧评分相同)
算法思路
双向贪心遍历:通过两次遍历分别处理左右相邻关系,确保每个孩子的糖果数满足条件。
核心步骤
- 初始化:每个孩子至少 1 颗糖果。
- 左→右遍历:若当前孩子评分高于左侧,则糖果数比左侧多 1。
- 右→左遍历:若当前孩子评分高于右侧,则糖果数取当前值与右侧加 1 的较大者。
- 累加求和:所有糖果数总和即为答案。
操作解析
- 左→右遍历:确保每个孩子比左侧评分高的糖果数正确。
- 右→左遍历:处理左侧评分高但未被左遍历覆盖的情况,通过取较大值避免破坏已满足的条件。
复杂度分析
- 时间复杂度:O(n),两次线性遍历。
- 空间复杂度:O(n),存储糖果数的数组。
代码实现
func candy(ratings []int) int {
n := len(ratings)
if n == 0 {
return 0
}
candies := make([]int, n)
// 初始每人至少1颗
for i := range candies {
candies[i] = 1
}
// 从左到右遍历,处理右边比左边高的情况
for i := 1; i < n; i++ {
if ratings[i] > ratings[i-1] {
candies[i] = candies[i-1] + 1
}
}
// 从右到左遍历,处理左边比右边高的情况
for i := n-2; i >= 0; i-- {
if ratings[i] > ratings[i+1] {
candies[i] = max(candies[i], candies[i+1] + 1)
}
}
// 计算总糖果数
total := 0
for _, c := range candies {
total += c
}
return total
}
func max(a, b int) int {
if a > b {
return a
}
return b
}
关键点总结
- 两次遍历:
- 左遍历确保每个孩子比左侧评分高时糖果正确。
- 右遍历修正比右侧评分高但未满足条件的情况。
- 取最大值操作:在右遍历时,需保持左遍历的结果不被破坏,仅当当前糖果数不足时才更新。
- 边界处理:
- 所有孩子初始至少 1 颗糖果。
- 当所有孩子评分相同时,每人仅需 1 颗。
示例解析
示例1:ratings = [1,0,2]
- 左遍历:
- 孩子0:1
- 孩子1:1(0 ≤ 1)
- 孩子2:1+1=2(2 > 0)
→
[1,1,2]
- 右遍历:
- 孩子1:1(0 ≤ 2)
- 孩子0:max(1, 1+1)=2(1 > 0)
→
[2,1,2]
- 总和:2+1+2=5。
示例2:ratings = [1,2,2]
- 左遍历:
- 孩子0:1
- 孩子1:1+1=2(2 > 1)
- 孩子2:1(2 ≤ 2)
→
[1,2,1]
- 右遍历:
- 孩子1:2(2 ≥ 1+1)
- 孩子0:1(1 ≤ 2)
→
[1,2,1]
- 总和:1+2+1=4。