原題:
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;
}