0


贪心算法每日一题(3)

一、柠檬水找零

在柠檬水摊上,每一杯柠檬水的售价为

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。

这道题做了很久了 不过时隔多日拿起来看其实思路还是很简单 !

题目中已经说明我们只有 5 ,10,20的钞票

先分三种情况:

1、顾客给了5块 我们不需要找零 把这5块存起来用于给别人找零

2、顾客给了10块 存取这10块用于找零 同时我们不得不使用5块给顾客找零 如果我们存储的5元钞票为0 那就返回false

3、顾客给了我们20 我们优先使用10块+5块给顾客找零 如果10块已经不够 那我们不得不使用3张五块 如果五块也不够返回false

题目表达明确 思路也很简单 一道不折不扣的贪心基础题(贪心策略很好找)、

代码:

public boolean lemonadeChange(int[] bills) {
        int ten=0;//一开始我们拥有的十元和五元数量都是0
        int five=0;
        for (int i=0;i<bills.length;i++){
            if (bills[i]==5){//5的时候只需要存 不存在找零
                five++;
            }else if (bills[i]==10){
                five--;//五块给顾客找零 存十块
                ten++;
                if (five<0){//如果一次找零不成功就直接返回false
                    return false;
                }
            }else {//顾客给了20的情况
                if (ten>0){
                    ten--;//还有十块 优先给十块
                    five--;
                }else {//没有十块了
                    five-=3;
                }
                if (ten<0||five<0){
                    return false;
                }
            }
        }
        return true;
    }

二、摆动序列

如果连续数字之间的差严格地在正数和负数之间交替,则数字序列称为** 摆动序列 。**第一个差(如果存在的话)可能是正数或负数。仅有一个元素或者含两个不等元素的序列也视作摆动序列。

  • 例如, [1, 7, 4, 9, 2, 5] 是一个 摆动序列 ,因为差值 (6, -3, 5, -7, 3) 是正负交替出现的。
  • 相反,[1, 4, 7, 2, 5][1, 7, 4, 5, 5] 不是摆动序列,第一个序列是因为它的前两个差值都是正数,第二个序列是因为它的最后一个差值为零。

子序列 可以通过从原始序列中删除一些(也可以不删除)元素来获得,剩下的元素保持其原始顺序。

给你一个整数数组

nums

,返回

nums

中作为 **摆动序列 **的 最长子序列的长度

示例 1:

**输入:**nums = [1,7,4,9,2,5]
**输出:**6
**解释:**整个序列均为摆动序列,各元素之间的差值为 (6, -3, 5, -7, 3) 。

示例 2:

**输入:**nums = [1,17,5,10,13,15,10,5,16,8]
**输出:**7
**解释:**这个序列包含几个长度为 7 摆动序列。
其中一个是 [1, 17, 10, 13, 10, 16, 8] ,各元素之间的差值为 (16, -7, 3, -3, 6, -8) 。

示例 3:

**输入:**nums = [1,2,3,4,5,6,7,8,9]
**输出:**2

这道题看起来似乎要删除一些结点来获得最长的摆动序列 ,但其实不用 因为我们要统计的只是个数(题解来源:代码随想录)

以例二举例:

局部最优:删除单调坡度上的节点(不包括单调坡度两端的节点),那么这个坡度就可以有两个局部峰值

整体最优:整个序列有最多的局部峰值,从而达到最长摆动序列

实际操作上,其实连删除的操作都不用做,因为题目要求的是最长摆动子序列的长度,所以只需要统计数组的峰值数量就可以了(相当于是删除单一坡度上的节点,然后统计长度)

这就是贪心所贪的地方,让峰值尽可能的保持峰值,然后删除单一坡度上的节点

本题代码实现中,还有一些技巧,例如统计峰值的时候,数组最左面和最右面是最不好统计的。

例如序列[2,5],它的峰值数量是2,如果靠统计差值来计算峰值个数就需要考虑数组最左面和最右面的特殊情况。

所以可以针对序列[2,5],可以假设为[2,2,5],这样它就有坡度了即preDiff = 0,如图:

针对以上情形,result初始为1(默认最右面有一个峰值),此时curDiff > 0 && preDiff <= 0,那么result++(计算了左面的峰值),最后得到的result就是2(峰值个数为2即摆动序列长度为2)

我们只需要记录峰值!(左右两边的特殊情况我们都已经做了处理)

代码:

public int wiggleMaxLength(int[] nums) {
        int res=1;
        int preDiff=0;//一开始的prediff设为0 其实也是为了处理最左边的特殊情况
        int curDiff=0;
        for (int i=1;i<nums.length;i++){
            curDiff=nums[i]-nums[i-1];//当前坡度
            if (preDiff<=0&&curDiff>0||preDiff>=0&&curDiff<0){
                res++;//峰值数量++
                preDiff=curDiff;
            }
        }
        return res;
    }

这道题似乎还有另一种简洁的写法 不过我自己本人并没有掌握这种高深的写法 这里仅分享出来

也希望有大佬教教我~

class Solution {
    public int wiggleMaxLength(int[] nums) {
        int n = nums.length;
        if (n < 2) {
            return n;
        }
        int up = 1;
        int down = 1;
        for (int i = 1; i < n; i++) {
            if (nums[i] > nums[i - 1]) {
                up = down + 1;
            }
            if (nums[i] < nums[i - 1]) {
                down = up + 1;
            }
        }
        return Math.max(up, down);
    }
}
标签: 算法 leetcode

本文转载自: https://blog.csdn.net/qq_59539549/article/details/123938007
版权归原作者 yan扬 所有, 如有侵权,请联系我们删除。

“贪心算法每日一题(3)”的评论:

还没有评论