leetcode-分发糖果

(记录leetcode让我头疼的题目)

级别:困难

题目:

n 个孩子站成一排。给你一个整数数组 ratings 表示每个孩子的评分。

你需要按照以下要求,给这些孩子分发糖果:

  • 每个孩子至少分配到 1 个糖果。
  • 相邻两个孩子评分更高的孩子会获得更多的糖果。

请你给每个孩子分发糖果,计算并返回需要准备的 最少糖果数目 。

解法1:两次遍历

思路

在满足题目要求的情况下,需要得到最少的糖果数量,所以在最开始我们就让所有孩子得到的糖果数量都等于1;然后再根据相邻孩子评分更高的孩子会获得更多的糖果进行两次遍历,一次向右遍历,查找向右递增的情况,然后在前一个孩子的数量上加1;一次向左遍历,查找向左递增的情况,找到递增的个数(也就是满足要求的最小情况,比如:4,3,2,1这种情况递增的数量就分别是4,3,2,1),找到后和第一次进行右遍历的结果进行比较取最大值就是当前孩子最终得到的糖果数量(比较取最大值的原因:因为此时有两个数分别是左遍历和右遍历得到的,要满足左右的情况都是相邻的孩子评分更高得到更多的糖果所以取最大值满足两边都可以)。

总结:

  • 向右遍历:当ratings [ i - 1 ] < ratings [ i ]时,i 号孩子的糖果数量将比 i- 1 号孩子的糖果数量 +1
  • 向左遍历:当ratings [ i ] > ratings [ i + 1 ]时,i 号孩子的糖果数量将比 i + 1 号孩子的糖果数量 +1

图解

代码

public int candy(int[] ratings) {
        int n = ratings.length;
        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;
            }
        }
        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);
        }
        return ret;
    }

解法2:常数空间遍历

(我最头疼的一个解法)

思路

主要解决的就是两个问题的糖果如何分配得到最低:

  • 递增序列:只需要在前一个的数量上 + 1 即可;
  • 递减序列:相当于将数据翻转之后也就是一个递增序列,所以只需要按照递增序列的思路即可,但是需要考虑一种情况:递增序列的长度和递减序列的长度相等,此处需要进行额外的 + 1 确保在递增到递减的转化过程中出现递减了但是分配到的糖果数量相同了。

图解

代码

public int candy2(int[] ratings) {
        int n = ratings.length;
        // 记录总的需要的糖果数量
        int ret = 1;
        /* pre:
                用于在递增序列中记录前一个分配到的糖果数量
        dec:
                记录递减序列的序列长度
        inc:
                记录递增序列的序列长度
        */
        int inc = 1, dec = 0, pre = 1;
        for (int i = 1; i < n; i++) {
            if (ratings[i] >= ratings[i - 1]) {
                // 判断非绝对递增的情况
                dec = 0;
                //当前同学和上一个同学分数相等时,直接分配1个就行,这样满足最小,否则就在前一个的数量上加一
                pre = ratings[i] == ratings[i - 1] ? 1 : pre + 1;
                ret += pre;
                inc = pre; // 记录递增序列的长度,此处最好表示的就是直接用pre进行赋值,因为pre本身就代表了递增序列的长度
            } else {
                // 对递减序列进行处理
                dec++; // 计量递减序列的长度
                if (dec == inc) {
                    //当递减序列长度和递增序列长度相等时,把递增序列的最后一个同学分配到递减序列中
                    dec++;
                }
                ret += dec;
                pre = 1;
            }
        }
        return ret;
    }

最后实在看不明白的可以往leetcode去看:135. 分发糖果 - 力扣(LeetCode)

(但是希望不大,我自己痛苦了已经转化比较好理解的解释了)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值