leetcode 135. 分发糖果

题目

老师想给孩子们分发糖果,有 N 个孩子站成了一条直线,老师会根据每个孩子的表现,预先给他们评分。

你需要按照以下要求,帮助老师给这些孩子分发糖果:

每个孩子至少分配到 1 个糖果。
相邻的孩子中,评分高的孩子必须获得更多的糖果。

那么这样下来,老师至少需要准备多少颗糖果呢?

示例 1:

输入: [1,0,2]
输出: 5
解释: 你可以分别给这三个孩子分发 2、1、2 颗糖果。

示例 2:

输入: [1,2,2]
输出: 4
解释: 你可以分别给这三个孩子分发 1、2、1 颗糖果。
第三个孩子只得到 1 颗糖果,这已满足上述两个条件。

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/candy

分析

这道题我一开始想的就是上升流下降流,结果就是搞不了上升下降怎么转换判断,本来我想的是比如123432这样的变成一个数组,[4,2],就是上升4个,下降2个这样的,如果这样其实也就不用记录在数组里了,只和上一个流有关,但是我就是不会转换,上升下降的转换.就看答案去了(/(ㄒoㄒ)/~~)

1(超时)

思路

首先给所有的元素赋值1.
对于每一个元素向左向右判断是否ratings大,但是给的糖果少,如果少就给自己变成+1
如 ratings[i]>ratings[i-1] &&candy[i]<=candy[i-1]
->candy[i]=candy[i-1]+1;
ratings[i]>ratings[i+1]&&candy[i]<=candy[i+1]
->candy[i]=candy[i+1]+1;
注意在遍历到i的时候,处理要以i元素为中心,因为是从1开始的,所以遇到ratings打的元素是小的元素+1,而不是大的元素-1;
每次处理完成后,看此次处理是否没有影响candy数组,没有影响就说明调整的正好了,直接退出计算糖果数就可以了.
这个方法的复杂度是O(n2)以上的,具体不会算.但是超时了.

代码

//44/48
class Solution {
public:
    int ans=0;
    int candy(vector<int>& ratings) {
        int sz=ratings.size();
        if(sz==1) return 1;
        vector<int> dd(sz,1);
        bool flag=true;
        while(flag)
        {
            flag=false;
            if(ratings[0]>ratings[1]&&dd[0]<=dd[1])
            {
                dd[0]=dd[1]+1;
                flag=true;
            }
            for(int i=1;i<sz-1;i++)
            {
                if(ratings[i]>ratings[i+1]&&dd[i]<=dd[i+1])
                {
                    dd[i]=dd[i+1]+1;
                    flag=true;
                }
                if(ratings[i]>ratings[i-1]&&dd[i]<=dd[i-1])
                {
                    dd[i]=dd[i-1]+1;
                    flag=true;
                }
            }
            if(ratings[sz-1]>ratings[sz-2]&&dd[sz-1]<=dd[sz-2])
            {
                dd[sz-1]=dd[sz-2]+1;
                flag=true; 
            } 
        }
        int ans=0;
        for(int i=0;i<sz;i++)
        {
            ans+=dd[i];
        }
        return ans;
    }
};

2(过)

思路

这个思路是计算每个位置最小是多少.然后填写,每个位置的最小是多少由左右比他小的连续的序列的长度决定,比如4123,3左边的最小的是123这个序列,所以left[3]=3,由于右边没有元素,所以right[3]=1,在求这个元素多少糖的时候,就是max(left[i],right[i]),因为要满足两边的条件.这样其实也是我一开始思路计算连续长度的一种转化,转化成左右两个方向.

代码

class Solution {
public:
    int ans=0;
    int candy(vector<int>& ratings) {
        int sz=ratings.size(),tmp=1,ans=0;
        vector<int> left(sz,1),right(sz,1);
        for(int i=1;i<sz;i++)
        {
            if(ratings[i-1]<ratings[i]) tmp++;
            else tmp=1;
            left[i]=tmp;
        }
        tmp=1;
        for(int i=sz-2;i>=0;i--)
        {
            if(ratings[i+1]<ratings[i]) tmp++;
            else tmp=1;
            right[i]=tmp;
        }
        for(int i=0;i<sz;i++)
        {
            ans+=max(left[i],right[i]);
        }
        return ans;
    }
};

ps:这种解法让我想起我在算法笔记里的一道题.其实可以只用一个数组,是在计算完left[sz]数组后,从右向左遍历,一边计算右边的长度,直接进行这个位置的数字的计算.省了一遍的O(n)遍历具体如下:
代码(改了一点的)

//假的吧,这个比上面的还慢!
class Solution {
public:
    int ans=0;
    int candy(vector<int>& ratings) {
        int sz=ratings.size(),tmp=1,ans=0;
        vector<int> left(sz,1);
        for(int i=1;i<sz;i++)
        {
            if(ratings[i-1]<ratings[i]) tmp++;
            else tmp=1;
            left[i]=tmp;
        }
        tmp=1;
        ans+=max(left[sz-1],1);
        for(int i=sz-2;i>=0;i--)
        {
            if(ratings[i+1]<ratings[i]) tmp++;
            else tmp=1;
            ans+=max(tmp,left[i]);
        }
        return ans;
    }
};

3

思路

观看官方题解:
这个思路和我一开始的不谋而合,就是看流,然后计算每一个流,不过这个思路是好想,但是实现比较难(至少对我来说).
对于局部的分配,都是一个上升或者下降的趋势,如1234321,有两个趋势1234,4321.每个趋势都是可以通过n*(n+1)/2来计算所有趋势里的糖果数的.
有两个处理难点,一个是峰谷或者谷底的归属,还有上下的转换.

  • 峰点很好想到,这个极点归属于长度长的趋势,短的趋势不受到糖数的约束,就按自己少的计算就可以了
  1. 如1234531,5归属12345,31自己不受约束计算就是长度2->2*(2+1)/2=3个糖.
  2. 谷底就是1,两边都算32134,1两边的长度都要算.
    计算按照上升-下降这样的趋势来计算,在一次的上升下降趋势后,来看峰顶归上升流还是下降流
    对于一个相等的处理就是当成两端,只不过有一段只有下降/上升
class Solution {
public:
    int ans=0;
    int cal(int p){return p*(p+1)/2;}
    int candy(vector<int>& ratings) {
        int sz=ratings.size();
        if(sz<=1) return sz;
        int ans=0,up=0,down=0,last=0;
        for(int i=1;i<sz;i++)
        {
            int now=ratings[i]-ratings[i-1];
            if((last>0&&now==0)||(last<0&&now>=0))
            {
                ans+=cal(up)+cal(down)+max(up,down);
                up=down=0;
            }
            if(now>0) up++;
            if(now<0) down++;
            if(now==0) ans++;
            last=now;
        }
        ans+=cal(up)+cal(down)+max(up,down)+1;
        return ans;
    }
};
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值