leetcode 135. 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?

Example 1:

Input: [1,0,2]
Output: 5
Explanation: You can allocate to the first, second and third child with 2, 1, 2 candies respectively.

Example 2:

Input: [1,2,2]
Output: 4
Explanation: You can allocate to the first, second and third child with 1, 2, 1 candies respectively.
             The third child gets 1 candy because it satisfies the above two conditions.

 

=============================2018.12.21更新============================

从左到右贪心的策略选择,注意后面下降的峰值,影响到之前那一个上升时的峰值。

class Solution {
    public int candy(int[] ratings) {
        int cur=1,sum=1,downpeak=1,uppeak=1;//下降时的downpeak,uppeak是上升时顶峰,cur为当前大小
        for(int i=1;i<ratings.length;i++){
            if(ratings[i]>ratings[i-1]){
                downpeak=0;
                cur++;
                uppeak=cur;
                sum+=cur;
            }else if(ratings[i]<ratings[i-1]){
                if(cur==1){
                    downpeak++;
                    sum+=downpeak;
                    if(downpeak==uppeak){
                        downpeak++;
                        sum++;
                    }
                }else{
                    downpeak=1;
                    sum+=downpeak;
                    cur=1;
                }
            }else{
                downpeak=1;
                uppeak=1;
                sum++;
                cur=1;
            }
        }
        return sum;
    }
}

=================================================================

思路:观察可看出:从左到右遍历。新加入的孩子分3中情况:

1.新加入的孩子i的级别如果比左边孩子级别大,每次初始当前孩子的糖果数val[i-1]+1。这样能使当前糖果总数最少。(证明:当前点与该递增子序列第一个开始递增的点之间的点的和是最小,子序列第一个递增点之前的所有点的和又是最小的。下同)

2.新加入的孩子i的级别如果比左边孩子级别小,每次初始当前孩子的糖果数1。这样能使糖果总数最少,导致左边孩子糖果数的更新。

3.新加入的孩子i的级别如果与左边孩子相等,每次初始当前孩子的糖果数1。这样能使糖果总数最少,不会导致左边孩子糖果数的更新。

开始的想法是定义一个val[i]数组记录每个孩子的糖果数,遇到上面第二种情况,会导致之前孩子的糖果数更新,所以时间复杂度为O(n^2),空间复杂度O(n),虽然AC了,但时间太长了。

(未改进的代码)

 public int candy(int[] ratings) {
        int[] val=new int[ratings.length];
        int sum=0;
        val[0]=1;
        for(int i=1;i<ratings.length;i++){
            if(ratings[i]<=ratings[i-1]){
                val[i]=1;
                for(int j=0;i-j-1>=0;j++){//整理之前的数的值
                    if(ratings[i-j]<ratings[i-j-1]&&val[i-j]>=val[i-j-1]){
                        val[i-j-1]++;
                    }else{
                        break;                       
                    }
                }
            }
            else{
                val[i]=val[i-1]+1;
            }
        }
        for(int i=0;i<val.length;i++){
            sum+=val[i];
        }
        return sum;
    }

 

后来再次观察得,其实不用维护一个数组保存每个孩子的糖果数。后面孩子糖果数的更新取决于前面某个孩子的最高峰值。只需保存,当前孩子单调递减是左边的峰值high,和当前孩子与左边某个峰值的距离dis,这两个值决定了之间孩子的糖果数的更新情况,如果dis==high的时候要更新峰值high。还需保存当前糖果和sum。

分3种情况:

1.当孩子级别递增时,根据每个孩子的糖果数比前面那个孩子的+1,更新sum,并记录最高峰值。

2.当孩子级别递减时,因为孩子每次糖果初始值为1,导致左边孩子糖果数的更新,根据左边的峰值high和dis,更新sum。

3.当孩子级别从递减,转变为递增;或孩子级别与前一个相等时。初始化峰值high,距离dis,需定义一个标记位rise和第一种情况做区分。当rise==false就是第2或第3种情况,rise==true就是第一种情况。别忘了更新sum。

时间复杂度O(n),空间复杂度O(1)。

(改进后的代码)

public int candy(int[] ratings) {
        int high=1;
        int dis=0;//下降时到峰值的距离
        int sum=1;
        boolean rise=true;//true
        for(int i=1;i<ratings.length;i++){//上升
            if(ratings[i]>ratings[i-1]){
                if(rise){//保持上升
                   high++;
                   sum+=high;//每次更新high
                }else{//前面下降碰到上升,初始化
                    rise=true;
                    high=2;
                    sum+=2;
                    dis=0;
                }
            }
            else if(ratings[i]<ratings[i-1]){//下降
                rise=false;//下降标志位
                dis++;
                if(dis==high){
                    high++;//之前最大高度增加1 
                    sum+=dis+1;
                }else{
                    sum+=dis;
                }
            }else{//当ratings[i]==ratings[i-1]时,看成 ratings[i]之后是下降状态
               rise=false;
               high=1;
               sum+=1;
               dis=0;
            }
        }
        return sum;
    }

 

看到网上还有两种思路,这两种贪心思想需要慢慢领会。参考了http://blog.sina.com.cn/s/blog_6dad631f0102wzhm.html

1.从权值最小的小孩开始分起,每分到一个小孩时,看他是否比身边小孩的权值大,如果大,则比身边小孩最多糖数再多1;如果小,则不变。参考http://www.cnblogs.com/shadowmydx/p/4914215.html

2.先初始化candy数组为1; 对rating数组从左到右扫描,如果遇到rating[j]=candy[j+1],则让candy[j+1]=candy[j]+1; 对rating数组从右到左扫描,如果遇到rating[j-1]>rating[j]且candy[j-1]<=candy[j],则让candy[j-1]=candy[j]+1;这样左右邻居都满足要求了。参考leetcode官方解答https://leetcode.com/articles/candy/#approach-4-single-pass-approach-with-constant-space-accepted。又看了下官方解答第四种思路和上述解法差不多。

第二种方法难以想到,需要慢慢体会。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值