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?

题目解析:

本以为“等级”高的分配的个数一定比等级低的高。如果是这样的话,排序后,就很容易计算。

但是题目不是这个意思,是等级高的比相邻的等级低的分配的个数多。比如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;
}

点赞