老师想给孩子们分发糖果,有 N 个孩子站成了一条直线,老师会根据每个孩子的表现,预先给他们评分。
你需要按照以下要求,帮助老师给这些孩子分发糖果:
- 每个孩子至少分配到 1 个糖果。
- 相邻的孩子中,评分高的孩子必须获得更多的糖果。
那么这样下来,老师至少需要准备多少颗糖果呢?
示例 1:
输入: [1,0,2]
输出: 5
解释: 你可以分别给这三个孩子分发 2、1、2 颗糖果。
示例 2:
输入: [1,2,2]
输出: 4
解释: 你可以分别给这三个孩子分发 1、2、1 颗糖果。
第三个孩子只得到 1 颗糖果,这已满足上述两个条件。
解题思路采用的是贪心算法,分别从两左右边取出每个位置上所需糖果数量,然后比较得出最优的结果。
建立两个数组用来存放从左边遍历出的糖果数和从右边遍历出的糖果数量。
class Solution {
public int candy(int[] ratings) {
int len=ratings.length;
int[] left=new int[len];
int[] right=new int[len];
for(int i=1;i<len;i++){//左侧开始循环,统计出左侧比当前位置小的数量
if(ratings[i]>ratings[i-1])
left[i]=1+left[i-1];
else//比当前位置大或者等于,则相当于从零开始
left[i]=0;
}
for(int i=len-2;i>=0;i--){//右侧开始循环,统计当前位置右侧比自己小的数量
if(ratings[i]>ratings[i+1])
right[i]=1+right[i+1];
else
right[i]=0;
}
int result=0;
for(int i=0;i<len;i++){
result+=left[i]>right[i]?left[i]:right[i];
}
return result+len;
}
}
后来看了优化算法,循环次数减少了貌似效率提高了,这里记录一下思路,主要是将数组分为两种大致的情况,递增/相等 和 递减,递增就直接加上了,相等默认为1,等到递减结束后,也就是遇到递增时,将上一个递减序列算出来,并判断是否有必要修改上一个递增序列尾数的大小。具体代码实现如下:
int len=ratings.length;
int prev=1;//记录上一个递增序列的最后一个数字,也就是最大的那个数
int count=0;//存储分数连续降低的个数
int result=1;//记录所需的糖果数
for(int i=1;i<len;i++){//从左向右比较大小,找出递减序列
if(ratings[i]>=ratings[i-1]){//判断是否为递减,分为两种情况之前是递增或者相等和递减两种分支
//这部分是判断出递增,每个递增都会先判断之前是否有递减序列,然后计算出糖果数
if(count>0){//表明之前的都是递减的,计算递减的序列应该分的糖果数
result+=count*(count+1)/2;
if(count>=prev)//如果上一个递增的最后的数字小于等于递减数列的数量
result+=count-prev+1;//让prev的那个数字增大到count+1,就需要弥补一些数,即count+1-prev
count=0;//重置
prev=1;//重置
}
//计算当前位置应该分的糖果数
if(ratings[i]==ratings[i-1]){//如果相等,默认当前位置为1,将prev置为1
result+=1;
prev=1;
}else{
result+=prev+1;//这种情况为递增,直接增加prev+1
prev++; //将递增数列的尾值+1
}
}else{
count++;//如果为递减则将计数器count+1
}
}
if(count>0){//最后的几个是递减的
result+=count*(count+1)/2;
if(count>=prev)
result+=count-prev+1;
}