代码随想录 贪心算法-简单题目

目录

455.分发饼干 

1005.K次取反后最大化的数组和

860.柠檬水找零


455.分发饼干 

455. 分发饼干

简单

假设你是一位很棒的家长,想要给你的孩子们一些小饼干。但是,每个孩子最多只能给一块饼干。

对每个孩子 i,都有一个胃口值 g[i],这是能让孩子们满足胃口的饼干的最小尺寸;并且每块饼干 j,都有一个尺寸 s[j] 。如果 s[j] >= g[i],我们可以将这个饼干 j 分配给孩子 i ,这个孩子会得到满足。你的目标是尽可能满足越多数量的孩子,并输出这个最大数值。

示例 1:

输入: g = [1,2,3], s = [1,1]
输出: 1
解释: 
你有三个孩子和两块小饼干,3个孩子的胃口值分别是:1,2,3。
虽然你有两块小饼干,由于他们的尺寸都是1,你只能让胃口值是1的孩子满足。
所以你应该输出1。

示例 2:

输入: g = [1,2], s = [1,2,3]
输出: 2
解释: 
你有两个孩子和三块小饼干,2个孩子的胃口值分别是1,2。
你拥有的饼干数量和尺寸都足以让所有孩子满足。
所以你应该输出2.

提示:

  • 1 <= g.length <= 3 * 104
  • 0 <= s.length <= 3 * 104
  • 1 <= g[i], s[j] <= 231 - 1

用可满足该孩子的最小饼干去喂该孩子 

class Solution {  
    // 方法用于找到可以匹配的孩子和饼干的数量  
    public int findContentChildren(int[] g, int[] s) {  
        // 对孩子的胃口大小数组和饼干大小数组进行排序  
        Arrays.sort(g);  
        Arrays.sort(s);  
  
        // begin变量用于记录饼干数组当前检查的起始位置  
        int begin = 0;  
        // count变量用于记录成功匹配的孩子和饼干的数量  
        int count = 0;  
  
        // 遍历孩子的胃口大小数组  
        for(int i = 0; i < g.length; i++){  
            // 从begin开始遍历饼干大小数组  
            for(int j = begin; j < s.length; j++){  
                // 如果找到一块饼干的大小大于等于当前孩子的胃口  
                if(s[j] >= g[i]){  
                    // 更新begin为当前饼干的后一个位置,这样下次就不需要再检查这块饼干  
                    begin = j + 1;  
                    // 匹配成功,count加1  
                    count++;  
                    // 跳出内层循环,继续检查下一个孩子的胃口  
                    break;  
                }    
            }  
        }  
        // 返回成功匹配的孩子和饼干的数量  
        return count;  
    }  
}

1005.K次取反后最大化的数组和

1005. K 次取反后最大化的数组和

简单

给你一个整数数组 nums 和一个整数 k ,按以下方法修改该数组:

  • 选择某个下标 i 并将 nums[i] 替换为 -nums[i] 。

重复这个过程恰好 k 次。可以多次选择同一个下标 i 。

以这种方式修改数组后,返回数组 可能的最大和 。

示例 1:

输入:nums = [4,2,3], k = 1
输出:5
解释:选择下标 1 ,nums 变为 [4,-2,3] 。

示例 2:

输入:nums = [3,-1,0,2], k = 3
输出:6
解释:选择下标 (1, 2, 2) ,nums 变为 [3,1,0,2] 。

示例 3:

输入:nums = [2,-3,-1,5,-4], k = 2
输出:13
解释:选择下标 (1, 4) ,nums 变为 [2,3,-1,5,4] 。

提示:

  • 1 <= nums.length <= 104
  • -100 <= nums[i] <= 100
  • 1 <= k <= 104

排序后取反k次,优先取反负数,负数全部取反后,排序得到最小的非负数,对其进行k次取反后得到最大的sum 

class Solution {  
    // 方法用于计算在数组nums上进行k次取反操作后可能得到的最大和  
    public int largestSumAfterKNegations(int[] nums, int k) {  
        int sum = 0;  
          
        // 先对数组进行排序,这样负数会排在前面  
        Arrays.sort(nums);  
  
        // 遍历数组,将负数取反(如果k还有剩余并且当前数字是负数)  
        for(int i = 0; i < nums.length; i++){  
            if(k > 0 && nums[i] < 0){  
                nums[i] = -nums[i]; // 取反当前负数  
                k--; // k剩余次数减1  
            }  
        }  
  
        // 再次对数组进行排序,因为取反操作可能改变了数组的顺序  
        Arrays.sort(nums);  
  
        // 如果k还有剩余且为奇数,则将最小的数取反(这样可以得到最大和)  
        if(k > 0 && k % 2 == 1){  
            nums[0] = -nums[0];  
        }  
  
        // 计算排序后数组的总和  
        for(int i = 0; i < nums.length; i++){  
            sum += nums[i];  
        }  
  
        // 返回计算得到的最大和  
        return sum;  
    }  
}

 笨蛋写法,每次取反都取最小的

class Solution {
    public int largestSumAfterKNegations(int[] nums, int k) {
        
        int sum = 0;
        for(int i = 0; i < k; i++){
            Arrays.sort(nums);
            nums[0] = -nums[0];   
        }
        for(int i = 0; i < nums.length; i++){
            sum += nums[i];
        }
        return sum;
    }
}

860.柠檬水找零

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。

提示:

  • 1 <= bills.length <= 105
  • bills[i] 不是 5 就是 10 或是 20 

我的思路:因为美元10只能给账单20找零,而美元5可以给账单10和账单20找零,美元5更万能!所以找零优先用10元面值 

class Solution {  
    // 判断是否能通过5元和10元找零满足所有顾客  
    public boolean lemonadeChange(int[] bills) {  
        // 使用一个数组来统计各个面额的纸币数量,数组下标代表纸币面额,从1到20  
        int[] count = new int[21];  
          
        // 遍历每张纸币  
        for(int i = 0; i < bills.length; i++){  
            // 当前纸币的面额出现次数加1  
            count[bills[i]] += 1;  
              
            // 需要的找零金额,初始值为当前纸币面额减去5
            int change = bills[i] - 5;  
              
            // 当需要找零的金额大于等于10,并且还有10元纸币可用时  
            while(change >= 10 && count[10] > 0){  
                // 使用一张10元纸币找零  
                count[10] -= 1;  
                // 更新找零金额,减去10  
                change -= 10;  
            }  
              
            // 当剩余的找零金额大于等于5,并且还有5元纸币可用时  
            while(change >= 5 && count[5] > 0){  
                // 使用一张5元纸币找零  
                count[5] -= 1;  
                // 更新找零金额,减去5  
                change -= 5;  
            }  
              
            // 如果找零金额仍然不为0,说明当前纸币无法找零  
            if(change != 0){  
                return false;  
            }  
        }  
          
        // 如果所有纸币都能成功找零,返回true  
        return true;  
    }  
}

 找零有如下三种情况:

  • 情况一:账单是5,直接收下。
  • 情况二:账单是10,消耗一个5,增加一个10
  • 情况三:账单是20,优先消耗一个10和一个5,如果不够,再消耗三个5

此时大家就发现 情况一,情况二,都是固定策略,都不用我们来做分析了,而唯一不确定的其实在情况三。

而情况三逻辑也不复杂甚至感觉纯模拟就可以了,其实情况三这里是有贪心的。

账单是20的情况,为什么要优先消耗一个10和一个5呢?

因为美元10只能给账单20找零,而美元5可以给账单10和账单20找零,美元5更万能!

所以局部最优:遇到账单20,优先消耗美元10,完成本次找零。全局最优:完成全部账单的找零。

class Solution {  
    public boolean lemonadeChange(int[] bills) {  
        // five表示当前拥有的5元纸币数量  
        int five = 0;  
        // ten表示当前拥有的10元纸币数量  
        int ten = 0;  
  
        // 遍历顾客支付的每张纸币  
        for (int i = 0; i < bills.length; i++) {  
            // 如果当前纸币是5元  
            if (bills[i] == 5) {  
                // 增加5元纸币的数量  
                five++;  
            }   
            // 如果当前纸币是10元  
            else if (bills[i] == 10) {  
                // 需要找零5元,因此减少5元纸币的数量  
                five--;  
                // 增加10元纸币的数量  
                ten++;  
            }   
            // 如果当前纸币是20元  
            else if (bills[i] == 20) {  
                // 优先使用10元和5元纸币找零  
                if (ten > 0) {  
                    // 如果有10元纸币,则使用一张10元纸币找零  
                    ten--;  
                    // 同时使用一张5元纸币找零  
                    five--;  
                } else {  
                    // 如果没有10元纸币,则使用三张5元纸币找零  
                    five -= 3;  
                }  
            }  
            // 如果在找零过程中5元或10元纸币数量变为负数,说明无法找零,返回false  
            if (five < 0 || ten < 0) return false;  
        }  
          
        // 如果所有纸币都能成功找零,返回true  
        return true;  
    }  
}

  • 39
    点赞
  • 51
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值