[leetcode] Candy

原題:

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?

 

Solution (1)

這題本身可以用貪心法來做,我們用candy[n]表示每個孩子的糖果數,遍歷過程中,如果孩子i+1的rate大於孩子i 的rate,那麼當前最好的選擇自然是:給孩子i+1的糖果數=給孩子i的糖果數+1

如果孩子i+1的rate小於等於孩子i 的rate咋整?這個時候就不大好辦了,因爲我們不知道當前最好的選擇是給孩子i+1多少糖果。

解決方法是:暫時不處理這種情況。等數組遍歷完了,我們再一次從尾到頭遍歷數組,這回逆過來貪心,就可以處理之前略過的孩子。

最後累加candy[n]即得到最小糖果數。

 

這種解法是需要O(n)的輔助空間給candy[]的。

有沒有更好的辦法?

Solution (2) 此方法以及代碼部分參考了Shangrila 的方法。

請回想一下:我們爲什麼需要輔助空間?當孩子的rate是一個非遞減曲線的時候,我們是不需要輔助空間的,比如5個孩子的rate分別是1,2,5,7,10。那麼糖果數自然是1,2,3,4,5。又如5個孩子的rate分別是1,2,5,5,10,那麼糖果數自然是1,2,3,1,2。

因此如果rate是非遞減數列,我們可以精確計算出當前孩子應該給多少糖果,把這個糖果數加入總數即可。

當孩子的rate出現遞減的情況該如何是好?不用輔助空間能處理嗎?

假設5個孩子的rate是 1,5,4,3,2。我們這樣計算:遍歷時,第一個孩子依然糖果爲1,第二個孩子糖果爲2,第三個孩子糖果給幾個?我們遍歷到後面就會知道第二個孩子給的糖果太少了,應該給4個。有沒有辦法在遍歷到後面時,能計算出一個修正值,使得加上這個修正值,正好依然可以使總糖果數是正確的?

 

其實這個修正值不難計算,因爲可以發現遞減數列的長度決定了第二個孩子該給幾個糖果。仔細觀察:遍歷到第四個孩子時我們知道了第二個孩子不該給2,應該給3,因此Total 要 +=1;遍歷到第五個孩子我們知道了第二個孩子不該給3得給4,因此Total 要 += 1。我們設一個變量beforeDenc表示進入遞減序列之前的那個孩子給的糖果值,再設置length用來表達當前遞減序列的長度。這兩個變量就可以決定Total是不是要修正:當遍歷第三個孩子的時候 beforeDenc = 2,以後每遍歷一個孩子,因爲length已經超過了beforeDenc,每次Total都要額外+1,來修正第二個孩子的糖果數。

 

對於後面三個孩子,我們可以這樣計算:遍歷到第三個孩子,因爲這是遞減數列的第二個數字,我們Total += 1;第四個孩子是遞減數列的第三個數字,Total += 2;第五個孩子是遞減數列的第四個數字,Total += 3。

可以發現最後三個孩子的糖果總數依然是正確的,雖然Total 每次增加的糖果數量正好和當前孩子得到的糖果數是反序關係。

 

這種邊遍歷邊修正的方法可以保證一次遍歷,不需要O(n)空間下計算出Total的正確值。

代碼:

int candy(vector<int> &ratings) {
    int Total = 0;    /// Total candies
    int length = 0;  /// Continuous descending length of rate
    int nPreCanCnt = 1; /// Previous child's candy count
    int beforeDenc = nPreCanCnt;
    if(ratings.begin() != ratings.end())
    {
        Total++; //Counting the first child's candy (1).
        for(vector<int>::iterator i = ratings.begin()+1; i!= ratings.end(); i++)
        {
            if(*i < *(i-1))
            {
                length++;
                if(beforeDenc <= length)
                {
                    Total++;
                }
                Total += length;
                nPreCanCnt = 1;    //This step is important, it ensures that once we leave the decending sequence, candy number start from 1
            }
            else
            {
                int curCanCnt = 0;
                if(*i > *(i-1))
                { 
                    curCanCnt = (nPreCanCnt + 1);
                }
                else
                {
                    curCanCnt = 1;
                }
                Total += curCanCnt;
                nPreCanCnt = curCanCnt;
                length = 0;    //reset length of decending sequence
                beforeDenc = curCanCnt;
            }
        }
    }
    return Total;
}


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章