原題:
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) 此方法以及代碼部分參考了 的方法。
請回想一下:我們為什么需要輔助空間?當孩子的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; }