Candy

本文探讨了在给定儿童评分的情况下,如何以最小糖果数满足每个孩子的糖果需求,确保评分高的孩子比邻居获得更多的糖果。提出了两种解决方案,一种使用辅助数组达到O(n)的时间复杂度,另一种通过遍历修正的方法同样实现O(n)的时间复杂度但降低了空间复杂度。

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?

public class Solution {
    public int candy(int[] ratings) {
        int[] sum = new int[ratings.length];
        for(int i = 0;i < ratings.length; i ++){
            sum[i]=1;//每一个孩子最小一颗糖
        }
        for(int i = 1; i < ratings.length;i ++){
            if(ratings[i] > ratings[i-1] && sum[i] <= sum[i-1]){//调整左邻居
                sum[i] = sum[i-1]+1;
            }
        }
        for(int i = ratings.length-2; i >= 0 ; i--){
            if(ratings[i] > ratings[i+1] && sum[i] <= sum[i+1]){//调整右邻居
                sum[i] = sum[i+1]+1;
            }
        }
        int count = 0;
        for(int i = 0;i < ratings.length; i ++){
            count += sum[i];
        }
        return count;
    }
}

Runtime: 424 ms

public class Solution {
    public int candy(int[] ratings) {
        int[] sum = new int[ratings.length];
        for(int i = 1; i < ratings.length;i ++){
            if(ratings[i] > ratings[i-1] && sum[i] <= sum[i-1]){//调整左邻居
                sum[i] = sum[i-1]+1;
            }
        }
        for(int i = ratings.length-2; i >= 0 ; i--){
            if(ratings[i] > ratings[i+1] && sum[i] <= sum[i+1]){//调整右邻居
                sum[i] = sum[i+1]+1;
            }
        }
        int count = ratings.length;
        for(int i = 0;i < ratings.length; i ++){
            count += sum[i];
        }
        return count;
    }
}
Runtime: 707 ms

怪事,我少了一个for循坏的初始化,反而运行慢了。。。。。

以上算法时间复杂度为O(n),空间复杂度为O(n)

改进:参考自http://www.cnblogs.com/felixfang/p/3620086.html

请回想一下:我们为什么需要辅助空间?当孩子的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的正确值。

public class Solution {
    public int candy(int[] ratings) {
        int len = 0;//记录降序的长度
        int count = 1;//当前孩子的糖果数
        int dec = 0;//降序之前最近孩子的糖果数
        int total = 1;
        for(int i = 1 ; i < ratings.length ; i ++){
            if(ratings[i] > ratings[i-1]){//升序
                if(len > 0){//前面有一段降序
                    if(len >= dec){//需要修正糖果数
                        total += (len - dec + 1);
                    }
                    count = 2;
                    len = 0;
                    dec = 0;
                }else{
                    count ++;
                }
                total += count;
            }else if(ratings[i] == ratings[i-1]){//相等序列时孩子糖果数为1
                if(len > 0){//前面有一段降序
                    if(len >= dec){//需要修正糖果数
                        total += (len - dec + 1);
                    }
                    len = 0;
                    dec = 0;
                }
                count = 1;
                total += count;
            }else{//降序
                len ++;
                dec = count;
                total += len;
            }
        }
        if(len > 0 && len >= dec){//最后一个孩子位于一个降序中
            total += (len - dec + 1);
        }
        return total;
    }
}

Runtime: 445 ms

将代码简洁下

public class Solution {
    public int candy(int[] ratings) {
        int len = 0;//记录降序的长度
        int count = 1;//当前孩子的糖果数
        int dec = count;//降序之前最近孩子的糖果数
        int total = 1;
        for(int i = 1 ; i < ratings.length ; i ++){
            if(ratings[i] < ratings[i-1]){//降序
                len ++;
                if(dec <= len){
                    total++;
                }
                total += len;
                count = 1;
            }else{//非递减排序
                if(ratings[i] > ratings[i-1]){//升序
                    count++;
                }else{
                    count = 1;
                }
                total += count;
                len = 0;
                dec = count;
            }
        }
        return total;
    }
}

可能是网络问题,明明感觉是一个不错的方法,但是时间性能上没有什么特别的提高,不过C++的源程序确实很快。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值