算法通关村-----原来贪心如此简单

贪心思想

贪心本质上是花费更少的资源获得更多的收益,在解决问题时,我们希望在每一步中都进行最优的选择,从而的到最优的结果。其实贪心并不总是能得到最优的结果,但是可以得到近似最优解,如果我们对最优结果的要求并不高,但是希望时间成本小一些,贪心往往是一个不错的选择。什么时候应该考虑使用贪心呢,是当问题具有最优子结构的时候,但是最优子结构这个问题比较复杂,我们可以从题目中寻找贪心的规律。

分发饼干

问题描述

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

问题分析

每个孩子最多只能给一块饼干,我们希望的每一步的最优解,就是将当前最大尺寸的饼干,分配给该饼干能满足的最大胃口的孩子,这样可以避免“大饼干”分配给“胃口小孩子”的浪费。

代码实现

public int findContentChildren(int[] g, int[] s) {
    Arrays.sort(g);
    Arrays.sort(s);
    int j = s.length-1;
    int count = 0;
    for(int i=g.length-1;i>=0;i--){
        if(j>=0 && s[j]>=g[i]){
            count++;
            j--;
        }
    }
    return count;
}

柠檬水找零

问题描述

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

问题分析

如果是五元,直接收下,如果是十元,用五元找零,如果是二十元,可以用一张十元加一张五元或者三张五元找零。当收到一张二十元时,应该如何处理呢,很明显,如果有十元,用一张十元加一张五元,否则三张五元,这就是我们每一步的最优解

代码实现

public boolean lemonadeChange(int[] bills) {
    int cash5 = 0;
    int cash10 = 0;
    for(int i=0;i<bills.length;i++){
        if(bills[i]==5){
            cash5++;
        }else if(bills[i]==10){
            if(cash5>=1){
                cash10++;
                cash5--;
            }else{
                return false;
            }
        }else{
            if(cash10>=1&&cash5>=1){
                cash10--;
                cash5--;
            }else if(cash5>=3){
                cash5-=3;
            }else{
                return false;
            }
        }
    }
    return true;
}

分发糖果

问题描述

n 个孩子站成一排。给你一个整数数组 ratings 表示每个孩子的评分。你需要按照以下要求,给这些孩子分发糖果:

每个孩子至少分配到 1 个糖果。

相邻两个孩子评分更高的孩子会获得更多的糖果。

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

问题分析

如果两个相邻评分不等,则一个孩子比另一个孩子多一个糖果,如果两个孩子评分相等,则给第二个孩子一颗糖果,这就是每一次的最优解,如果直接按照规则,需要不断比较一个孩子的评分和两侧孩子的评分,我们可以通过两次遍历实现,第一次,设置第一个孩子一颗糖果,后面如果评分递增,则分配给孩子多一个糖果,否则,只给一个糖果,然后从倒数第二个孩子向前遍历,如果比后一个孩子评分高,则增加一个糖果,只是第二次遍历需要同时考虑两侧,所以取两者的较大值即可。

代码实现

public int candy(int[] ratings) {
    int[] nums = new int[ratings.length];
    nums[0] = 1;
    int count = 0;
    for(int i=1;i<nums.length;i++){
        if(ratings[i]>ratings[i-1]){
            nums[i] = nums[i-1]+1;
        }else{
            nums[i] = 1;
        }
    }
    for(int i=nums.length-2;i>=0;i--){
        if(ratings[i]>ratings[i+1]){
            nums[i] = Math.max(nums[i+1]+1,nums[i]);
        }
    }
    for(int i=0;i<nums.length;i++){
        count+=nums[i];
    }
    return count;
}

你可能感兴趣的:(算法训练营,算法,数据结构,贪心算法,java)