アルゴリズム演習ポスト--38--Candyの配布(Java)

キャンディーを配る

1.はじめに

先生はお菓子を子供たちに配りたいと思っています。N人の子供たちが一直線に並んでいて、先生はそれぞれの子供たちのパフォーマンスに基づいて事前に評価します。

次の要件に従って、教師がこれらの子供たちにキャンディーを配布するのを手伝う必要があります。

每个孩子至少分配到 1 个糖果。
评分更高的孩子必须比他两侧的邻位孩子获得更多的糖果。

では、先生はいくつのキャンディーを用意する必要がありますか?

注:题目没有讲清如果两个小孩子分数相等的情况如何分配糖果数,
在题解当中是后一个小孩子分配的糖果数要少于或者等于前一个相同分数的小孩子.

比如:
输入:[2,2,2,2,2]      答案:5。每个小孩子分配1个
输入:[1,3,3,4,5]      答案:9。分配规则:1、2、1、2、3

(件名:LeetCode

二、解決策

1.欲張りアルゴリズム
ここに画像の説明を挿入

アイデア:スコア配列には、昇順、フラット、降順(さまざまな処理方法に対応)の3つの可能性があります。これら3つにそれぞれ貪欲であり、これら3つのシーケンスをトラバースした後、対応するキャンディーの総数を取得できます。

package com.lxf.test;

public class Candy {
    
    
    public static void main(String[] args) {
    
    
        //1,0,2,4,2,6,7,9,5,3
        //1,3,2,2,1
        //1,0,2
        //1,2,87,87,87,2,1
        System.out.println(candy(new int[]{
    
    0}));
    }
    public  static    int candy(int[] ratings) {
    
    
        //糖果数
        int candyNum=0;
        //升序基数
        int base1=1;
        //降序基数
        int base2=0;
        int length=ratings.length-1;
        if(length<0) return 0;
        if(length==0) return 1;
        for (int i = 0; i < length; ) {
    
    
            //升序处理,candyNum直接++(注意:没包含第一个数字)
            //第一个数字放到降序中处理,而第一次升序特殊(前面没有降序队列)
            //注意此时糖果数已经将下一个降序队列的第一个数字加上了
            while(i<length &&ratings[i]<ratings[i+1]){
    
    
                candyNum+=++base1;
                i++;
            }
            //第一次升序需+1
            if(base1==i+1&&candyNum>0){
    
    
                candyNum+=1;
            }
            //如果是平序,直接+1,也有注意:开头平序和全部数字平序特殊需+1
            while(i< length&&ratings[i]==ratings[i+1]){
    
    
                candyNum+=1;
                base1=1;
                i++;
            }
            //降序处理,base2++
            while(i< length&&ratings[i]>ratings[i+1]){
    
    
                base2++;
                i++;
            }
            //从base2一直加到1,就相当于将降序队列全部包括
            //比如:1、2、3、6、5、4、3、2、1、4、7
            //其中6 5 4 3 2 1为降序队列,这种方法确保降序全部包含,也包含了下一次升序队列(1,4,7)的1
            int k=base2;
            while (k>0){
    
    
                candyNum+=k;
                k--;
            }
            if(base2!=i){
    
    
                //不是第一次降序就要判断是否超过了前面队列的最后一个值
                //比如分数数组:1 2 5 4 3 2 1
                //前一步分配糖果数为: 1 2 3 4 3 2 1
                //5分数对应糖果数为3
                //4分数对应糖果数为4。这不符合规则

                //5需要增加糖果数:
                //此时base1对应就是5的糖果数.而base2对应的就是4的糖果数
                //base2-base1+1就是5需要增加的糖果数(确保分数高的分到的糖果数越多)
                candyNum+=base2<base1?0:base2-base1+1;
            }else{
    
    
                //第一次降序需加上开头第一个数字
                candyNum+=i+1;
            }
            //将降序和升序置回
            base2=0;
            base1=1;
        }
        if(ratings[0]==ratings[1]){
    
    
            //开头平序和全部数字平序特殊需+1
            candyNum+=1;
        }
        return candyNum;
    }
}

最初の昇順またはフラット順で1が失われ、最初の降順でi + 1が失われることに気付きました。いずれの場合も、i + 1をiに変更するか、1を追加し、最後にキャンディーを初期値にします。の数は1に設定されます。
2.欲張りアルゴリズムの小さな最適化:

package com.lxf.test;

public class Candy {
    
    
    public static void main(String[] args) {
    
    
        //1,0,2,4,2,6,7,9,5,3
        //1,3,2,2,1
        //1,0,2
        //1,2,87,87,87,2,1
        System.out.println(candy(new int[]{
    
    0}));
    }
    public  static    int candy(int[] ratings) {
    
    
        //糖果数
        int candyNum=1;
        //升序基数
        int base1=1;
        //降序基数
        int base2=0;
        int length=ratings.length-1;
        if(length<0) return 0;
        if(length==0) return 1;
        for (int i = 0; i < length; ) {
    
    
            //升序处理,candyNum直接++(注意:没包含第一个数字)
            //第一个数字放到降序中处理,而第一次升序特殊(前面没有降序队列)
            //注意此时糖果数已经将下一个降序队列的第一个数字加上了
            while(i<length &&ratings[i]<ratings[i+1]){
    
    
                candyNum+=++base1;
                i++;
            }
            //如果是平序,直接+1,也有注意:开头平序和全部数字平序特殊需+1
            if(i< length&&ratings[i]==ratings[i+1]){
    
    
                candyNum+=1;
                base1=1;
                i++;
            }
            //降序处理,base2++
            while(i< length&&ratings[i]>ratings[i+1]){
    
    
                base2++;
                i++;
            }
            //从base2一直加到1,就相当于将降序队列全部包括
            //比如:1、2、3、6、5、4、3、2、1、4、7
            //其中6 5 4 3 2 1为降序队列,这种方法确保降序全部包含,也包含了下一次升序队列(1,4,7)的1
            int k=base2;
            while (k>0){
    
    
                candyNum+=k;
                k--;
            }
            if(base2!=i){
    
    
                //不是第一次降序就要判断是否超过了前面队列的最后一个值
                //比如分数数组:1 2 5 4 3 2 1
                //前一步分配糖果数为: 1 2 3 4 3 2 1
                //5分数对应糖果数为3
                //4分数对应糖果数为4。这不符合规则

                //5需要增加糖果数:
                //此时base1对应就是5的糖果数.而base2对应的就是4的糖果数
                //base2-base1+1就是5需要增加的糖果数(确保分数高的分到的糖果数越多)
                candyNum+=base2<base1?0:base2-base1+1;
            }else{
    
    
                //第一次降序需加上开头第一个数字(本来是i+1,改成i,保证了第一次不管是哪种序列都要丢个1)
                candyNum+=i;
            }
            //将降序和升序置回
            base2=0;
            base1=1;
        }
        return candyNum;
    }
}

3. 2つのトラバーサル(公式ソリューション)

class Solution {
    
    
    public int candy(int[] ratings) {
    
    
        int n = ratings.length;
        //存储左规则数(也即升序),匹配到右规则不赋值按平序-->1
        int[] left = new int[n];
        for (int i = 0; i < n; i++) {
    
    
            if (i > 0 && ratings[i] > ratings[i - 1]) {
    
    
                left[i] = left[i - 1] + 1;
            } else {
    
    
            	//平序数
                left[i] = 1;
            }
        }
        //right:左规则(匹配降序),匹配到左规则不赋值按平序-->1
        //ret:总糖果数
        int right = 0, ret = 0;
        for (int i = n - 1; i >= 0; i--) {
    
    
            if (i < n - 1 && ratings[i] > ratings[i + 1]) {
    
    
                right++;
            } else {
    
    
            	//平序数
                right = 1;
            }
            //增加右规则(包含平序)和左规则(包含平序)的最大值(很妙)
            //这一句相当于贪心算法的三段浓缩
            ret += Math.max(left[i], right);
            //比如一个分值数组:1 2 3 3 5 7 6 4 2
    		//left数组: 1 2 3 1 2 3 1 1 1
    		//right变量:1 1 1 1 1 4 3 2 1  
    		//ret糖果数:1 2 3 1 2 4 3 2 1=19
        }
        return ret;
    }
}

4.貪欲な単純化(公式ソリューション)
方法1の簡略版に相当し、支援する変数がもう1つあります

class Solution {
    
    
    public int candy(int[] ratings) {
    
    
        int n = ratings.length;
        //总糖果数
        int ret = 1;
        //inc:前一个升序的最大糖果数(判断降序第一个是否超过了前一个升序的最大值)
        //dec:降序基数  pre:升序基数
        int inc = 1, dec = 0, pre = 1;
        for (int i = 1; i < n; i++) {
    
    
            if (ratings[i] >= ratings[i - 1]) {
    
    
            	//匹配升序或平序
                dec = 0;
                //平序+1,升序pre++
                pre = ratings[i] == ratings[i - 1] ? 1 : pre + 1;
                //ret奖赏
                ret += pre;
                inc = pre;
            } else {
    
    
            	//匹配降序
                dec++;
                //降序超过了,前一个升序的最大数字要++(加到desc就行,一样的结果)
                if (dec == inc) {
    
    
                    dec++;
                }
                //加上dec降序的糖果数
                ret += dec;
                pre = 1;
            }
        }
        return ret;
    }
}

おすすめ

転載: blog.csdn.net/Inmaturity_7/article/details/111658561