分糖果问题

694 阅读2分钟
原文链接: www.androidniffler.com

一群孩子做游戏,现在请你根据游戏得分来发糖果,要求如下:
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;
 }