Description

n 個孩子站成一排,每人有一個評分 ratings[i]。你需要分配糖果,規則:每人至少 1 顆;相鄰兩人中評分較高的必須拿到更多糖果。回傳所需的最少糖果總數。

Example:

Input: ratings = [1,0,2] Output: 5 (分配 [2,1,2])

Intuition#

兩次遍歷:先從左到右處理右邊比左邊大的情況,再從右到左處理左邊比右邊大的情況。

  • 單方向遍歷無法同時滿足左右兩邊的約束
  • 從左到右:如果 ratings[i] > ratings[i-1],則 candy[i] = candy[i-1] + 1
  • 從右到左:如果 ratings[i] > ratings[i+1],則 candy[i] = max(candy[i], candy[i+1] + 1)
  • 取兩次的最大值確保兩個方向的約束都滿足

Approaches#

1: 兩次遍歷#

  • 概念: 第一次從左到右確保右鄰比左鄰大時糖果更多,第二次從右到左確保左鄰比右鄰大時糖果更多。
  • 時間複雜度: O(n)
  • 空間複雜度: O(n)
class Solution {
    fun candy(ratings: IntArray): Int {
        val n = ratings.size
        val candies = IntArray(n) { 1 }

        // 從左到右
        for (i in 1 until n) {
            if (ratings[i] > ratings[i - 1]) {
                candies[i] = candies[i - 1] + 1
            }
        }

        // 從右到左
        for (i in n - 2 downTo 0) {
            if (ratings[i] > ratings[i + 1]) {
                candies[i] = maxOf(candies[i], candies[i + 1] + 1)
            }
        }

        return candies.sum()
    }
}

⭐2: 一次遍歷(斜坡計數)#

  • 概念: 追蹤上升和下降斜坡的長度。上升時糖果遞增;下降時回頭補糖果。
  • 時間複雜度: O(n)
  • 空間複雜度: O(1)
class Solution {
    fun candy(ratings: IntArray): Int {
        val n = ratings.size
        if (n <= 1) return n

        var total = 1
        var up = 0
        var down = 0
        var peak = 0

        for (i in 1 until n) {
            when {
                ratings[i] > ratings[i - 1] -> {
                    up++
                    down = 0
                    peak = up
                    total += up + 1
                }
                ratings[i] < ratings[i - 1] -> {
                    down++
                    up = 0
                    // 如果下降長度超過峰值,峰值也需要加 1
                    total += down + if (down > peak) 1 else 0
                }
                else -> {
                    up = 0
                    down = 0
                    peak = 0
                    total += 1
                }
            }
        }
        return total
    }
}

🔑 Takeaways#

  • Pattern: 雙向貪心 — 分別處理左右約束,取最大值合併
  • 關鍵技巧: 兩次遍歷(左到右 + 右到左)是處理「相鄰元素雙向約束」的經典模式;一次遍歷需要追蹤斜坡和峰值