一群孩子做游戏,现在请你根据游戏得分来发糖果,要求如下:
1,每个孩子不管得分多少,起码分到1个糖果
2,任意两个相邻的孩子之间,得分较多的孩子必须拿多一些的糖果。
给定一个数组arr 代表得分数组,请返回最少需要多少糖果。
例如:arr[1,2,2],糖果分配为[1,2,1],即可满足要求且数量最少,所以返回4。
[要求]
arr 长度为N, 时间复杂度为O(N),额外空间复杂度为O(1)
解答
先引入爬坡和下坡的概念,从左到右依次考虑每个孩子,如果一个孩子的有邻居比他大,那么爬坡过程开始,如果一直单调递增,就一直爬坡,否则爬坡结束。下坡开始,如果一直单调递减,就一直下坡,直到遇到一个孩子的右邻居大于或等于他,则下坡结束。爬坡中的叫左坡,下坡中的叫右坡。
定义了爬坡过程和下坡过程后,大家可以看到arr数组可以被分解成很多对左坡和右坡数组,利用左坡和右坡来看糖果如何分,假设有一堆左坡和右坡,分别为[1,4,5,9]和[9,3,2]。对左坡来说,从左到右分的糖果应该是[1,2,3,4],对右坡来说应该[3,2,1],坡度是指坡中出去相同数字之后的序列长度,而根据我们定义的爬坡和下坡的过程,左坡和右坡不可能存在重复数字,所以坡度就是各自的序列长度。[1,2,3,3]
坡度为4,[3,2,1]坡度为3 如果左边的坡度更大,坡顶就按左坡的分配,如果右坡的坡度更大,就按右坡的分配,所以最终的分配为[1,2,3,4,2,1]
完整实现代码(最终实现时间复杂度为O(N) 空间复杂度为O(1) )
public int candy(int[] arr){
if(arr==null ||arr.length==0){
return 0;
}
int index=nextMinIndex(arr,0);
int res=rightCands(arr,0,index++);
int lbase=1;
int next=0;
int rcands=0;
int rbase=0;
while(index != arr.length){
if(arr[index]>arr[index-1]){
res+=++base;
index++;
}else if(arr[index]<arr[index-1]){
next=nextMinIndex(arr,index-1);
rcands=rightCands(arr,index-1,next++);
rbase=next-index+1;
res+=rcands+(rbase>lbase?-lbase:-rbase);
lbase=1;
index=next;
}else{
res+=1;
lbase=1;
index++;
}
}
return res;
}
public int nextMinIndex(int[] arr,int start){
for(int i=start;i!=arr.length-1;i++){
if(arr[i]<=arr[i+1]){
return i;
}
}
return arr.length-1;
}
public int rightCands(int[] arr, int left , int right)
{
int n=right-left+1;
return n+n*(n-1)/2;
}