算法---LeetCode 135. 分发糖果

1. 题目

原题链接

老师想给孩子们分发糖果,有 N 个孩子站成了一条直线,老师会根据每个孩子的表现,预先给他们评分。
你需要按照以下要求,帮助老师给这些孩子分发糖果:

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

那么这样下来,老师至少需要准备多少颗糖果呢?

示例 1:

输入:[1,0,2]
输出:5
解释:你可以分别给这三个孩子分发 2、1、2 颗糖果。

示例 2:

输入:[1,2,2]
输出:4
解释:你可以分别给这三个孩子分发 1、2、1 颗糖果。
第三个孩子只得到 1 颗糖果,这已满足上述两个条件。
Related Topics 贪心 数组
👍 573 👎 0

2. 题解

2.1 解法1: 两次遍历

根据题意, 可以定义 左规则和右规则,

  • 左规则: 若 rating[i-1]<rating[i], 则多给一颗糖给 i
  • 右规则: 若 rating[i-1]>rating[i], 则多给一颗糖给 i-1
算法流程:

1.先从左至右遍历学生成绩 ratings,按照以下规则给糖,并记录在 left 中:

1.先给所有学生一颗糖
2.从左至右遍历过程中, 若 rating[i-1]<rating[i], 则多给 i 一个糖

经过此规则分配后,可以保证所有学生糖数量 满足左规则 。

2.同理,在此规则下从右至左遍历学生成绩并记录在 right 中,可以保证所有学生糖数量 满足右规则。

3.最终,取以上 2 轮遍历 left 和 right 对应学生糖果数的 最大值 ,这样则 同时满足左规则和右规则 ,即得到每个同学的最少糖果数量。

这样做的原因: 如果从左至右一趟遍历想要同时满足左右规则, 那么是不可能的, 因为 i-1 的小朋友的糖果数目会变化, 这样无法同时满足, 所以需要两趟遍历

代码:

    class Solution {
        public int candy(int[] ratings) {
            int[] lefts = new int[ratings.length];
            int[] rights = new int[ratings.length];
            Arrays.fill(lefts, 1);
            Arrays.fill(rights, 1);
            // 从左至右遍历, 满足左规则
            for (int i = 1; i < lefts.length; i++) {
                if (ratings[i - 1] < ratings[i]) {
                    lefts[i] = lefts[i - 1] + 1;
                }
            }
            // 从右往左遍历, 满足右规则
            for (int i = rights.length - 1; i >= 1; i--) {
                if (ratings[i - 1] > ratings[i]) {
                    rights[i - 1] = rights[i] + 1;
                }
            }
            // 遍历两个数组, 每个元素取较大者
            int ans = 0;
            for (int i = 0; i < lefts.length; i++) {
                ans += Math.max(lefts[i], rights[i]);
            }
            return ans;
        }
    }

优化:
1.不用预先填充数组, 可以在遍历过程中, 不符合左右规则的, 直接发一颗糖
2.在第二次从右往左遍历时, 可以同时计算结果

    class Solution {
        public int candy(int[] ratings) {
            int[] lefts = new int[ratings.length];
            int[] rights = new int[ratings.length];
            // 最后一个访问的元素必须是 lefts[lefts.length-1]
            for (int i = 0; i < lefts.length; i++) {
                if (i > 0 && ratings[i - 1] < ratings[i]) {
                    lefts[i] = lefts[i - 1] + 1;
                } else {
                    lefts[i] = 1;
                }
            }
            int ans = 0;
            // 最后一个访问的元素必须是 rights[0]
            for (int i = rights.length - 1; i >= 0; i--) {
                if (i < rights.length - 1 && ratings[i] > ratings[i + 1]) {
                    rights[i] = rights[i + 1] + 1;
                } else {
                    rights[i] = 1;
                }
                // 这里直接累计结果
                ans += Math.max(lefts[i], rights[i]);
            }
            return ans;
        }
    }

参考:
分发糖果 (贪心思想,线性复杂度,清晰图解)
官方题解

2.2 解法2: 一次遍历

讲解可参考: C++一次遍历!一次遍历!超级简单详细!

    class Solution {
        public int candy(int[] ratings) {
            int ans = 1;
            int inc = 1, dec = 0, last = 1;
            for (int i = 1; i < ratings.length; ++i) {
                if (ratings[i] >= ratings[i - 1]) {
                    last = ratings[i] == ratings[i - 1] ? 1 : last + 1;
                    ans += last;
                    inc = last;
                    dec = 0;
                } else {
                    ++dec;
                    if (dec == inc) ++dec;
                    ans += dec;
                    last = 1;
                }
            }
            return ans;
        }

    }

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值