参考链接
http://www.cnblogs.com/x1957/p/3371601.html
http://www.cnblogs.com/felixfang/p/3620086.html O(1) O(1)
题目描述
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?
题目分析
题目大概意思就是给你一些孩子的rating,这些孩子站成一个直线,相邻的孩子rating高的一定要比rating低的孩子的candy多
所有孩子至少有一个candy。
问最少发多少个candy(好抠)
最少呢,我们当然就是保证再满足限制条件下尽量少发candy给孩子。
那限制条件当然来自相邻的孩子。
如果我比你高,我就比你多1,最低的孩子为1.
从左往右扫瞄一次,波谷为1,然后依次加1到波峰,再到波谷时又是1
这样就满足所有rating高的比左边rating低的孩子的candy多了。
然后再从右往左来一次,一样的,不弱还要多个条件就是,因为刚才算了一次candy[i]了
这次candy[i]要取最大的才能同同时满足这两个条件。
然后就ok啦,两边都满足了!
class Solution {
public:
int candy(vector<int> &ratings) {
// Note: The Solution object is instantiated only once and is reused by each test case.
int cap = ratings.size();
int* candy = new int[cap];
fill(candy , candy + cap , 0);
int k = 1;
for(int i = 1 ; i < cap ; i++){
if(ratings[i] > ratings[i - 1]){
candy[i] = max(k++ , candy[i]);
}else{
k = 1;
}
}
k = 1;
for(int i = cap -2 ; i >= 0 ; i --){
if(ratings[i] > ratings[i + 1]){
candy[i] = max(k++ , candy[i]);
}else{
k = 1;
}
}
int ans = cap;
for(int i = 0 ; i < cap ; i++) ans += candy[i];
return ans;
}
};
请回想一下:我们为什么需要辅助空间?当孩子的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;
}