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?
题目解析:
本以为“等级”高的分配的个数一定比等级低的高。如果是这样的话,排序后,就很容易计算。
但是题目不是这个意思,是等级高的比相邻的等级低的分配的个数多。比如1,2,2 分配的个数是1-2-1。一共四个。这样的话就麻烦了。得好好研究序列找到规律。
方案一:
通过举例是个很好的研究方法。比如1,2,3,3,4 分配个数为1-2-3-1-2。递增的话就依次增加,如果碰到当前值比前一个小的或相等的,就变为1。但又出现一个问题,比如1,2,3,4,5,4,3,2 分配个数为1-2-3-4-5-3-2-1。我们该怎么去考虑?可以这样碰到arr[4] = 5后面的arr[5] = 4以后,先给4分配1个即num[5]=1,再往后遍历,碰到arr[6] = 3比arr[5] = 4还小,就给3赋值为1即num[6],然后对num[6…0]数据进行修正(arr[i]>arr[i+1] && num[i]<=num[i+1] ======> num[i] = num[i+1]+1)。
这种时间复杂度为O(n^2)比较高。
class Solution {
public:
int candy(vector<int> &ratings) {
int len = ratings.size();
int *num = new int[len];
if(len == 0)
return 0;
num[0] = 1;
for(int i = 0;i < len;i++){
if(ratings[i]>ratings[i-1])
num[i] = num[i-1]+1;
else{
num[i] = 1;
Ajust(ratings,i,num);
}
}
int sum = 0;
for(int i = 0;i < len;i++)
sum += num[i];
return sum;
}
void Ajust(vector<int> &ratings,int index,int *num){
for(int i = index-1;i>=0;i--){
if(ratings[i]>ratings[i+1] && num[i]<=num[i+1]){
num[i] = num[i+1]+1;
}else
break;
}
}
};
方案二:
如果我们第一趟走的时候,直接将arr[i]<=arr[i-1]时,num[i]=1。先不处理,然后反向遍历,进行一趟修正。这样时间复杂度就降为O(n)。其修正过程和上面的是一样的(arr[i]>arr[i+1] && num[i]<=num[i+1] ======> num[i] = num[i+1]+1)。
class Solution {
public:
int candy(vector<int> &ratings) {
int len = ratings.size();
int *num = new int[len];
if(len == 0)
return 0;
num[0] = 1;
for(int i = 0;i < len;i++){
if(ratings[i]>ratings[i-1])
num[i] = num[i-1]+1;
else
num[i] = 1;
}
Ajust(ratings,num);
int sum = 0;
for(int i = 0;i < len;i++){
sum += num[i];
}
return sum;
}
void Ajust(vector<int> &ratings,int *num){
for(int i = ratings.size()-2;i>=0;i--){
if(ratings[i]>ratings[i+1] && num[i]<=num[i+1]){
num[i] = num[i+1]+1;
}
}
}
};
方案三:
方案二已经很完美了,时间复杂度已经降到最低,但是空间复杂度为O(n)。能否将空间复杂度也降下来?
其实方案二中已经隐隐约约感觉到,当连续递增或递减的时候,分配的数据是:1、2、3、4….或…4、3、2、1。这些都是连续的,那么我们可以统计连续严格递增的个数,连续严格递减的个数n,然后n(n+1)/2。但是中间过度点要额外考察,比如:(1、2、5、4、3、2、1)这个5放在递增序列中为3,放在递减序列中为5。这就造成了差别。
不过网页有个好的方法,不过没用我给的公式。记录下递减开始前一个孩子要给的糖果数k,然后遍历记录递减序列的长度len,如果len>k+1,那么以后再遍历len还继续增加的话,增加新分配孩子的糖果数的同时,要额外加上1,代表递减开始的元素起先分配的值小了。
而对于递减序列,我们不必严格按照“等级高的比等级低的分配的多”,我们只是为了计算个数,那么“递减开始的第二个孩子”分配1个,第三个孩子分配两个。
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;
}