[LeetCode] Candy

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?

解题思路:

贪心算法,能少给就少给。最初的想法是用一个数组记录每个小孩应该给的糖果数,从左往右依次扫描,若当前小孩比左边的小孩得分高,则比左边的小孩多拿一个糖,若得分低(或相等),则只分配一个糖。但是要考虑到若左边的小孩若比自己得分高,但只有一个糖,当前小孩若比其分配的少,那么只能分配一个糖了。故可以调整左边的小孩分的糖数。维持左边已经分配的小孩的糖果数目。于是版本一的代码如下:

class Solution {
public:
    int candy(vector<int> &ratings) {
        int number = ratings.size();
        if(number == 0){
            return 0;
        }
        int* d = new int[number];   //存储前i个已经分配的糖的数目
        
        d[0] = 1;   //贪心,给第一个小孩先给一个糖
        
        for (int i=1; i<number; i++){
            if(ratings[i]<ratings[i-1]){    //当前小孩得分比左边的少,只给他一个糖,但是得修正,防止左边的已经是一个糖了
                d[i] = 1;
                for(int j=i-1; j>=0; j--){
                    if(d[j] == d[j+1] && ratings[j]>ratings[j + 1]){    //左边的小孩得分高,却只得到相同的糖,他不干了,得给他加糖
                        d[j] = d[j]+1;
                    }else{
                        break;
                    }
                }
            }else if(ratings[i] == ratings[i-1]){   //当前小孩得分与左边的相同,给他左边小孩同样多的糖
                d[i] = 1;
            }else{          //当前小孩得分比左边多,给他比左边多一个糖
                d[i] = d[i-1] + 1;
            }
        }
        
        int result = 0;
        for(int i=0; i<number; i++){
            result += d[i];
        }
        delete[] d;
        return result;
    }
};

但是出现了执行时间超时的情况。因为该方法的时间复杂度为O(n2),特别是当小孩得分是逆序的情况,执行的最慢。

于是想其他办法。上网查了一下,终于得知,其实可以左右两边分别扫描,从左往右扫描,只需记录每个小孩相对于左边小孩所获得的糖果。从右往左扫描,只需记录每个小孩相对于右边小孩获得的糖果数,然后两个数组对应元素取较大值即为结果。下面是代码:

class Solution {
public:
    int candy(vector<int> &ratings) {
        int number = ratings.size();
        if(number == 0){
            return 0;
        }
        int* d = new int[number];   //存储前i个已经分配的糖的数目
        
        //两边扫,取较大的
        
        //从左往右扫
        d[0] = 1;   //贪心,第一个小孩先给一个糖
        for (int i=1; i<number; i++){
            if(ratings[i]<=ratings[i-1]){    //当前小孩得分比左边的少,只给他一个糖
                d[i] = 1;
            }else{          //当前小孩得分比左边多,给他比左边多一个糖
                d[i] = d[i-1] + 1;
            }
        }
        
        //从右往左扫
        int* r = new int[number];
        r[number - 1] = 1;
        for(int i = number-2; i>=0; i--){
            if(ratings[i]<=ratings[i+1]){    //当前小孩得分比右边的少,只给他一个糖
                r[i] = 1;
            }else{          //当前小孩得分比右边多,给他比右边多一个糖
                r[i] = r[i+1] + 1;
            }
        }
        
        int result = 0;
        for(int i=0; i<number; i++){
            result += (d[i]>r[i]?d[i]:r[i]);
        }
        delete[] r;
        delete[] d;
        return result;
    }
};

上述代码中,只扫描了两遍,所以时间复杂度为O(n),用的空间为2*n。可以优化一下代码,使得用的空间为n,如下所示:

class Solution {
public:
    int candy(vector<int> &ratings) {
        int number = ratings.size();
        if(number == 0){
            return 0;
        }
        int* d = new int[number];   //存储前i个已经分配的糖的数目
        
        //两边扫,取较大的
        
        //从左往右扫
        d[0] = 1;   //贪心,第一个小孩先给一个糖
        for (int i=1; i<number; i++){
            if(ratings[i]<=ratings[i-1]){    //当前小孩得分比左边的少,只给他一个糖
                d[i] = 1;
            }else{          //当前小孩得分比左边多,给他比左边多一个糖
                d[i] = d[i-1] + 1;
            }
        }
        
        int result = 0;
        int lastCandyNumber = 1;    //从右往左扫面上一个孩子的糖果数目
        result += (d[number-1]>lastCandyNumber?d[number-1]:lastCandyNumber);
        //从右往左扫
        for(int i = number-2; i>=0; i--){
            if(ratings[i]<=ratings[i+1]){    //当前小孩得分比右边的少,只给他一个糖
                lastCandyNumber = 1;
            }else{          //当前小孩得分比右边多,给他比右边多一个糖
                lastCandyNumber++;
            }
            result += (d[i]>lastCandyNumber?d[i]:lastCandyNumber);
        }
        
        delete[] d;
        return result;
    }
};

相关的若与左右两边有关的问题,都可以考虑两边扫的方法。


二次刷题2015-10-10

class Solution {
public:
    int candy(vector<int>& ratings) {
        int result=0;
        
        int len = ratings.size();
        if(len == 0){
            return result;
        }
        vector<int> numsLeft(len, 0);
        numsLeft[0] = 1;
        for(int i = 1; i<len; i++){
            if(ratings[i]>ratings[i-1]){
                numsLeft[i] = numsLeft[i-1] + 1;
            }else{
                numsLeft[i] = 1;
            }
        }
        vector<int> numsRight(len, 0);
        numsRight[len-1] = 1;
        for(int i = len-2; i>=0; i--){
            if(ratings[i]>ratings[i+1]){
                numsRight[i] = numsRight[i+1] + 1;
            }else{
                numsRight[i] = 1;
            }
        }
        
        for(int i = 0; i<len; i++){
            result += max(numsLeft[i], numsRight[i]);
        }
        
        return result;
    }
};


转载请注明:康瑞部落 » [LeetCode] Candy

0 条评论

    发表评论

    电子邮件地址不会被公开。 必填项已用 * 标注