【算法】贪心算法:LeetCode 135 分发糖果 、LeetCode 860 柠檬水找零

LeetCode 135:分发糖果

(困难)

题目

描述
老师想给孩子们分发糖果,有 N 个孩子站成了一条直线,老师会根据每个孩子的表现,预先给他们评分。
你需要按照以下要求,帮助老师给这些孩子分发糖果:
每个孩子至少分配到 1 个糖果。
评分更高的孩子必须比他两侧的邻位孩子获得更多的糖果。
那么这样下来,老师至少需要准备多少颗糖果呢?

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

示例 2:
输入:[1,2,2]
输出:4
解释:你可以分别给这三个孩子分发 1、2、1 颗糖果。
第三个孩子只得到 1 颗糖果,这已满足上述两个条件

思路

本题要求“评分更高的孩子必须比他两侧的邻位孩子获得更多的糖果”,并且分发糖果最少的数量是多少,在我对此题进行初步验证时,题目其实还隐含一层意思,也就是当相邻两个孩子评分相等时,是可以容忍对方比自己糖果数多的情况,但不能容忍的是比自己评分低的还比自己糖果多。

我在做这道题时,想到的就是一种折线图的抽象,即纵坐标表示糖果数,横坐标表示孩子的编号,孩子的编号对应孩子的评分,评分高与左右两边的孩子,在此图中应在左右两点,包括自己所在点的坡峰,而最终的纵坐标序列,就是糖果分发数量的数组,而如果更新出这样一个纵坐标序列呢,因为至少每个孩子一个糖果,因此我们先初始化一个各元素为1的数组,之后从左到右更新数组,保证若是右边的孩子评分高,那么他的糖果数要比左边孩子大1个;之后再从右到左更新数组,保证若是左边的孩子评分高,那么他的糖果数要比右边孩子大1个,但是由于之前从左到右更新了一次数组,因此,可能会出现本身就满足大1个糖果数的情况,因此再作比较并判断即可。
在这里插入图片描述

在我做这道题时,犯了一个错误,虽然想的是先从左到右更新一次,再从右到左更新一次,但是在实现时,错写成了如下图这种写法,这样会导致像是当 ratings 为 [1, 2, 3] 时,result 糖果的结果数组为 [1, 2, 2],而实际应为 [1, 2, 3],造成这种情况的原因是,我当时这种写法,其实只更新了一个方向。
在这里插入图片描述

实现

public class TX9分发糖果 {
    public int candy(int[] ratings) {
        int candySum = 0;
        int[] result = new int[ratings.length];
        Arrays.fill(result, 1);

        //从左->右
        for (int i = 0; i < ratings.length - 1; i++) {
            if (ratings[i + 1] > ratings[i]) { //右边大
                result[i + 1] = result[i] + 1;
            }
        }

        //从右->左
        for (int i = ratings.length - 1; i > 0; i--) {
            if (ratings[i - 1] > ratings[i]) { //左边大
                result[i - 1] = Math.max(result[i] + 1, result[i - 1]);
            }
        }

        for (int i = 0; i < result.length; i++) {
            candySum += result[i];
        }

        return candySum;
    }

    public static void main(String[] args) {
        TX9分发糖果 s = new TX9分发糖果();
        System.out.println("s.candy(new int[]{1, 0, 2}) = " + s.candy(new int[]{1, 0, 2}));
        System.out.println("s.candy(new int[]{1, 2, 87, 87, 87, 2, 1}) = " + s.candy(new int[]{1, 2, 87, 87, 87, 2, 1}));
    }
}

在这里插入图片描述

在这里插入图片描述

LeetCode 860:柠檬水找零

(简单)

题目

描述
在柠檬水摊上,每一杯柠檬水的售价为 5 美元。顾客排队购买你的产品,(按账单 bills 支付的顺序)一次购买一杯。
每位顾客只买一杯柠檬水,然后向你付 5 美元、10 美元或 20 美元。你必须给每个顾客正确找零,也就是说净交易是每位顾客向你支付 5 美元。
注意,一开始你手头没有任何零钱。
给你一个整数数组 bills ,其中 bills[i] 是第 i 位顾客付的账。如果你能给每位顾客正确找零,返回 true ,否则返回 false 。

示例1
输入:bills = [5,5,5,10,20]
输出:true
解释:
前 3 位顾客那里,我们按顺序收取 3 张 5 美元的钞票。
第 4 位顾客那里,我们收取一张 10 美元的钞票,并返还 5 美元。
第 5 位顾客那里,我们找还一张 10 美元的钞票和一张 5 美元的钞票。
由于所有客户都得到了正确的找零,所以我们输出 true。

示例 2:
输入:bills = [5,5,10,10,20]
输出:false
解释:
前 2 位顾客那里,我们按顺序收取 2 张 5 美元的钞票。
对于接下来的 2 位顾客,我们收取一张 10 美元的钞票,然后返还 5 美元。
对于最后一位顾客,我们无法退回 15 美元,因为我们现在只有两张 10 美元的钞票。
由于不是每位顾客都得到了正确的找零,所以答案是 false。

示例 3:
输入:bills = [5,5,10]
输出:true

示例 4:
输入:bills = [10,10]
输出:false

思路

本题是一道从生活逻辑上思考就可以解决的贪心算法题,属于入门级,数组元素值只有三种情况,即5、10、20,而每次收取5元,并将多余的找零,因此5是最小单元,20是最大单元,最小单元5可以用于给10元找零,给20元找零,而最大单元20则不能给任何付款找零,而10元则只能给20元找零,因此,本着贪心的思想,5是适用情况最多的元素,而10是适用情况第二多的元素,因此我们应该先消耗掉10,若仍需要找零,再去消耗适用情况更多的5即可,这就是其中的贪心思想

只需遍历一次数组,并动态记录5、10的当前个数,若顾客给5元,则不需找零,直接增加5的库存,若顾客给10元,则检查是否有5元,有则5的库存减1,10的库存加1,而如果付款是20元,若有10,那么应该先消耗掉一个10,再去消耗5元,若没有10,则只能去消耗适用情况更多的5元。

实现

public class TX10柠檬水找零 {
    public boolean lemonadeChange(int[] bills) {
        if (bills == null || bills.length == 0) {
            return false;
        }

        int fiveNum = 0;
        int tenNum = 0;
        //20元的不用统计,因为最大就给20,不能用于找零
        for (int i = 0; i < bills.length; i++) {
            if (bills[i] == 5) {
                fiveNum++;
            } else if (bills[i] == 10) {
                //要找5元
                if (fiveNum > 0) {
                    fiveNum--;
                    tenNum++;
                } else {
                    return false;
                }
            } else { //付20元时
                //要找15元(1:5+5+5   2:10+5)
                //优先用10找零
                if (tenNum > 0 && fiveNum > 0) {
                    tenNum--;
                    fiveNum--;
                } else if (fiveNum > 3) {
                    fiveNum -= 3;
                } else {
                    return false;
                }
            }
        }
        return true;
    }
}

在这里插入图片描述

  • 3
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

超周到的程序员

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值