力扣面试150题--分发糖果

day 8

题目描述

在这里插入图片描述

思路

初次思路:此题初看,从前向后遍历,发现无非考虑三种情况

  1. 如果这个孩子分数大于前一个孩子,则他的苹果数是前一个孩子苹果数加1。
  2. 如果这个孩子分数与前一个孩子分数相同,则他的苹果数为1。
  3. 如果这个孩子分数小于前一个孩子,就会存在两种情况:1)前一个孩子的苹果数大于1,则这个孩子的苹果数为1。2)前一个孩子的苹果数为1,就会出现问题。对于前一个孩子的苹果数为1,就需要往前回溯,将前一个孩子的苹果数加1,但是加一后不能确保前一个孩子满足要求。于是需要判断前一个孩子是否满足要求(由于前一个孩子是进行加1操作,所以唯一可能出现的问题是他的分数比前一个孩子低,但苹果数与前一个孩子相等或大,就得接着向前回溯),直到回溯到第一个满足题目要求的,就可以停止回溯了。
class Solution {
    public int candy(int[] ratings) {
        int sum=0;
        int n=ratings.length;
        int[] apple=new int[n];
        apple[0]=1;
        int i;
        for(i=1;i<ratings.length;i++){
            if(apple[i]!=0){//说明是回溯的
                //能回溯说明 对于这个孩子的下一个是满足条件的,只用判断与前一个孩子是否有矛盾
                if(i-1>=0){
                    if(ratings[i-1]>ratings[i]&&apple[i-1]<=apple[i]){
                        apple[i-1]=apple[i]+1;
                        i=i-2;
                    }
                }
            }
            else{
           if(ratings[i-1]>ratings[i]){//这个孩子比上一个孩子分数低
                if(apple[i-1]==1){//上一个孩子苹果为1,回溯
                    apple[i-1]++;//增加上一个孩子苹果树
                    i=i-2;
                }
                else{
                    apple[i]=1;
                }
           }
           else{
            if(ratings[i-1]==ratings[i]){//这个小孩与上一个分数相同
                apple[i]=1;
            }
            else{//这个小孩得分比上一个高
                apple[i]=apple[i-1]+1;
            }
           }
           }
        }
        for(i=0;i<ratings.length;i++){
            sum+=apple[i];
        }
        return sum;
    }
}

问题
在这里插入图片描述
时间复杂度很高,需要优化算法。
优化思路:官方题解提供了一个两次遍历的做法,思路如下

  • 将条件中「相邻的孩子中,评分高的孩子必须获得更多的糖果」拆分为两个规则
  • 左规则:当 ratings[i−1]<ratings[i] 时,i 号学生的糖果数量将比 i−1 号孩子的糖果数量多。
  • 右规则:当 ratings[i]>ratings[i+1] 时,i 号学生的糖果数量将比 i+1 号孩子的糖果数量多。
  • 做法:先从前向后遍历,让每个元素满足左规则,得到一个数组left,再从后向前遍历,让每个元素满足右规则,得到数组right(题目没用right数组,直接采取了right变量存储),最后每个孩子分的的实际苹果数是这个两个数组取最大值。
    个人理解
  • 可能会对其拆分为左右规则感到困惑:但根据我的初次想法可以发现,从前向后遍历,除了前一个元素比该元素分数小的情况,是不是都可以将该元素赋值为1.同理,从后向前遍历,除了下一个元素分数比该元素分数小,是不是可以将该元素赋值为1。(建议画张图理解下)
  • 为什么取的是左右规则实现后的最大值:这也很好理解,对于分别左右规则实现后每个元素得到的值而言,是分别相对于左右规则所得到的最小值,根据拆分的方式可知必须要使左右。如果取最小值,就可能导致左右规则中的一个不成立。那为什么要取最大值呢?我们再来细看以下左右规则,如果对于左规则而言,如果满足 ratings[i−1]<ratings[i] 的情况下,我们取最小值肯定是取上一个元素苹果数加1,那我们取上一个元素苹果数加2成立吗,当然成立。这就说明了取最大值不会影响左右规则。
class Solution {
    public int candy(int[] ratings) {
        int n = ratings.length;
        int[] left = new int[n];
        for (int i = 0; i < n; i++) {
            if (i > 0 && ratings[i] > ratings[i - 1]) {
                left[i] = left[i - 1] + 1;
            } else {
                left[i] = 1;
            }
        }
        int right = 0, ret = 0;
        for (int i = n - 1; i >= 0; i--) {
            if (i < n - 1 && ratings[i] > ratings[i + 1]) {
                right++;
            } else {
                right = 1;
            }
            ret += Math.max(left[i], right);
        }
        return ret;
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值