一道很有意思的题目,难度级别为 困难。
题目是这样的的
老师想给孩子们分发糖果,有 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))