[LeetCode] 135. Candy

題目鏈接: https://leetcode.com/problems/candy/description/

Description

There are N children standing in a line. Each child is assigned a rating value.

You are giving candies to these children subjected to the following requirements:

  • Each child must have at least one candy.
  • Children with a higher rating get more candies than their neighbors.

What is the minimum candies you must give?

解題思路

圖例

注: 下文所說極小值不是數學函數定義的極小值,只是與其有相似的性質,暫且對其命名爲極小值。並且,在此處不要求極小值左右相鄰的值必須比其大,而只需其小於左側相鄰的值,小於等於右側相鄰的值。上圖中五角星所標註的位置即爲此文所定義的極小值。

主要思路是找出ratings中所有的極小值,發給他們一顆糖,並從每一個極小值處,向左向右依次遞增糖的數量,直到rating開始下降停止更新。

這其中有一個關鍵地方需要注意,就是 rating 相等的兩個人挨在一起,糖的數量不需要一樣多,並且應該儘可能只給一顆糖,例如:

  • ratings [1, 2, 2, 3] -> candies [1, 2, 1, 2]
  • ratings [2, 3, 3, 2, 1] –> candies [1, 2, 3, 2, 1]
  • ratings [1, 2, 2, 2, 2, 1] –> candies [1, 2, 1, 1, 2, 1]

詳細過程爲,對於 ratings 從頭掃到尾尋找極小值,首尾單獨寫出來。每當找到一個極小值,向左向右更新糖的數量,由於尋找極小值時是從左向右掃,所以向左更新可能會遇到被前一個極小值向右更新過的個體,這兩個方向的做法有點區別,分開來講:

向右更新

可以大致分爲三種情況:

  • 一直單調遞增:糖的數量依次加一就可以;
  • 先增後平再增:平即是 rating 相等,此時會有兩個拐角,第一個拐角還是按照前面糖的數量加一,其後面rating相等的個體都只給一顆糖,第二個拐角後面繼續依次從兩顆糖開始遞增;
  • 先增後平再減:因爲在找到下一個極小值點時,會更新這種情況的第二個拐點,所以在此處可以與第二種情況一樣處理。

向左更新

只用考慮一種情況——單調遞增。即使遇到先增後平,也只考慮拐點那一個點,再往前的不需要修改,所以本質也是單調遞增。

向左更新的一個主要目的,就是完善前一個極小值點的漏洞,例如上圖中,紅橙交界點。

算法也很簡單,向左遍歷,不是遞增就終止;否則,判斷當前糖的數量是否小於右側糖的數量加一(遞增則從右往左依次加一),若小於則修改爲右側糖的數量加一,否則不變。

具體示例

如上圖,ratings 爲 [1, 2, 4, 3, 2, 1, 1, 2, 2, 3, 3, 3, 2, 2, 1, 3]

四個極小值點分別爲,(1, 1), (6, 1), (13, 2), (15, 1)

  1. (1, 1) 更新後,糖的數量分別爲 [1, 2, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
  2. (6, 1) 更新後,糖的數量分別爲 [1, 2, 4, 3, 2, 1, 1, 2, 1, 2, 1, 1, 0, 0, 0, 0]
  3. (13, 2) 更新後,糖的數量分別爲 [1, 2, 4, 3, 2, 1, 1, 2, 1, 2, 1, 2, 1, 1, 0, 0]
  4. (15, 1) 更新後,糖的數量分別爲 [1, 2, 4, 3, 2, 1, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2]

最終,糖的數量分別爲 [1, 2, 4, 3, 2, 1, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2],共計 28 顆。

Code

class Solution {
public:
    int candy(vector<int>& ratings) {
        int size = ratings.size();
        vector<int> candies(size, 0);

        if (size == 0 || size == 1) return size;

        // 首部單獨判斷是否爲極小值,向右更新糖的數量
        if (ratings[0] <= ratings[1]) {
            candies[0] = 1;

            int right = 1;
            while (right < size) {
                if (ratings[right] > ratings[right - 1]) {
                    candies[right] = candies[right - 1] + 1;
                } else if (ratings[right] == ratings[right - 1]) {
                    candies[right] = 1;
                } else {
                    break;
                }
                right++;
            }
        }

        // 從第二個開始尋找極小值,向左右兩邊更新糖的數量
        for (int pos = 1; pos < size - 1; ++pos) {
            if (ratings[pos] < ratings[pos - 1] && ratings[pos] <= ratings[pos + 1]) {
                candies[pos] = 1;

                int left = pos - 1;
                int right = pos + 1;

                while (left >= 0) {
                    if (ratings[left] > ratings[left + 1]) {
                        if (candies[left + 1] + 1 > candies[left]) candies[left] = candies[left + 1] + 1;
                    } else {
                        break;
                    }
                    left--;
                }

                while (right < size) {
                    if (ratings[right] > ratings[right - 1]) {
                        candies[right] = candies[right - 1] + 1;
                    } else if (ratings[right] == ratings[right - 1]) {
                        candies[right] = 1;
                    } else {
                        break;
                    }
                    right++;
                }
            }
        }

        // 尾部單獨判斷是否爲極小值,向左更新糖的數量
        if (ratings[size - 1] <= ratings[size - 2]) {
            candies[size - 1] = 1;

            int left = size - 2;
            while (left >= 0) {
                if (ratings[left] > ratings[left + 1] && candies[left + 1] + 1 > candies[left]) {
                    candies[left] = candies[left + 1] + 1;
                } else {
                    break;
                }
                left--;
            }
        }

        return accumulate(candies.begin(), candies.end(), 0);
    }
};
發佈了60 篇原創文章 · 獲贊 18 · 訪問量 8萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章