leetcode135.分发糖果

一道很有意思的题目,难度级别为 困难。
题目是这样的的
老师想给孩子们分发糖果,有 N 个孩子站成了一条直线,老师会根据每个孩子的表现,预先给他们评分。
你需要按照以下要求,帮助老师给这些孩子分发糖果:
每个孩子至少分配到 1 个糖果。
相邻的孩子中,评分高的孩子必须获得更多的糖果。
那么这样下来,老师至少需要准备多少颗糖果呢?
示例 1:
输入: [1,0,2]
输出: 5
解释: 你可以分别给这三个孩子分发 2、1、2 颗糖果。
示例 2:
输入: [1,2,2]
输出: 4
解释: 你可以分别给这三个孩子分发 1、2、1 颗糖果。
第三个孩子只得到 1 颗糖果,这已满足上述两个条件。

这题用贪心做,听说也可以用dp。
有一种策略是先每个小朋友发一颗糖,然后从左右各遍历一次补给评分高的同学,这样是可以的。
但是我借鉴了大神的更有趣的做法。
先举个例子。ratings = [1,3,5,7,6,5,4,3,2,1],下面讲一下思路。
首先我们从头开始,如果一路升序,比如这里的1,3,5,7,那么我们贪心的做,自然从第一个人给第一块糖开始,,每人多加一块,直到遇到了降序,注意,这里的降序很重要!因为我们需要尽可能的少给糖,但是需要避免的是,到最后一人无糖可发。
这个例子里面,到7这个小朋友的时候,我们给他4块糖,那么到6这个同学的时候,我们就给3块糖,然后依次递减,这时候我们会发现什么?到了3这个小朋友的时候,我们已经无糖可给,而如果给3小朋友1块或两块糖却又违背题目规则了。
题目有趣的地方就在这,那么我们该怎么做?
贪心策略就是先得到降序的位数,然后我们用等差求和,公差为1(毕竟我们是贪心的),算出递减这个阶段要给的糖的个数,在这个例子里,我们给最后一个小朋友 1块糖。因为他就是递减的末尾。不管递减末尾小朋友的评分为多少,他只有1块糖,这是本题的贪心策略。
重点来了,就是补糖,我们不可以让评分为7的小朋友拿到的糖比6少,但是7前面的5比6少就无所谓了,毕竟题目只要求相邻,例子里7 有4块糖,而6在从1一路加糖开始,已经有了6块,那7小朋友肯定就不乐意了,所以要补糖,补上的就是6-4+1 = 3颗,这时7就是7颗,比6多刚好多一颗。完美而又贪心。
这里的求和公式就是求1~n的和: sum = (1 + n) * n / 2.
还需要注意的是结尾的边界情况。这题就存在边界情况。
然后上代码。
这里贴出c和python3的。
其中python3在leetcode上测试通过的。

#include<stdio.h>
#define LENGTH(a) ((sizeof(a))/sizeof(a[0]))

int candy(int* ratings, int ratingsSize) 
{
    if(ratingsSize == 0)
    return 0;
    int dis = 0,pre = 1,res = 1,i; //dis为步长 
    for(i=1; i<ratingsSize; i++)
    {
    	if(ratings[i] >= ratings[i-1])
    	{
    		if(dis > 0)
    		{
    			res += dis * (dis + 1)/2;
    			if(dis >= pre)
    				res += dis - pre + 1;
    			dis = 0;
    			pre = 1;
			}
			if(ratings[i] == ratings[i-1])
				pre = 1;
			else //在条件ratings[i] >= ratings[i-1]之下,如果不等,即ratings[i] > ratings[i-1],此时只需要顺序加糖果 
				pre += 1; 
			res += pre;
		}
		else //倒序的时候 
		dis += 1;
	}
	if(dis > 0)
	{
		res += dis * (dis + 1)/2;
		if(pre <= dis)
		{
			res += dis - pre + 1;
		 } 
	}
	return (int)res;
}
int main()
{
	int ratings[] = {1,3,5,7,6,5,4,3,2,1};
	int ratingsSize = LENGTH(ratings);
	int ans = candy(ratings,ratingsSize);
	printf("%d",ans);
	return 0;
}
class Solution:
    def candy(self, ratings) :
        if not ratings:
            return 0
        res , pre , dis = 1 , 1 , 0 #dis 为保险的步长,避免后面的小朋友无糖可给,dis为至少要给多少才可保证有糖可以给
        for i in range(1,len(ratings)):
            if ratings[i] >= ratings[i-1]:
                if dis > 0:
                    res += dis * (dis + 1) / 2
                    if dis >= pre:#补给之前小朋友的糖
                        res += dis - pre + 1
                    dis = 0
                    pre = 1
                pre = 1 if ratings[i] == ratings[i - 1] else pre + 1
                res += pre
            else:
                dis += 1
        if dis > 0 : #最后的边界情况
            res += dis * (dis + 1) / 2
            if pre <= dis :
                res += dis - pre + 1
        return int(res)

if __name__ == "__main__":
    s = [1,3,5,7,6,5,4,3,2,1]
    print(Solution().candy(s))

在这里插入图片描述

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值