每日刷题|贪心算法初识-CSDN博客

阿里云国内75折 回扣 微信号:monov8
阿里云国际,腾讯云国际,低至75折。AWS 93折 免费开户实名账号 代冲值 优惠多多 微信号:monov8 飞机:@monov6

                                        食用指南本文为作者刷题中认为有必要记录的题目

                                        推荐专栏每日刷题

                                       ♈️今日夜电波悬溺—葛东琪

                                                                0:34 ━━━━━━️──────── 3:17
                                                                       ◀️   ⏸   ▶️    ☰ 

                                      关注点赞收藏您的每一次鼓励都是对我莫大的支持


目录

贪心算法的理解

一、分发饼干

 二、K次取反后最大化的数组和

三、柠檬水找零


贪心算法的理解

本文参考了一位大佬的题解详细的介绍了贪心算法链接

贪心算法一般分为如下四步

  • 将问题分解为若干个子问题
  • 找出适合的贪心策略
  • 求解每一个子问题的最优解
  • 将局部最优解堆叠成全局最优解

        贪心算法并没有固定的套路

        难点在于如何通过局部最优推出整体最优


一、分发饼干

455. 分发饼干icon-default.png?t=N7T8https://.cn/problems/assign-cookies/

题目描述 

        假设你是一位很棒的家长想要给你的孩子们一些小饼干。但是每个孩子最多只能给一块饼干。对每个孩子 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(vector<int>& g, vector<int>& s) {
        sort(g.begin(),g.end());//排序
        sort(s.begin(),s.end());
        int index=s.size()-1;
        int sum=0;
        for(int i=g.size()-1;i>=0;i--)//从大往小尽量给
        {
            if(index>=0&&s[index]>=g[i])
            {
                index--;
                sum++;
            }
        }
     
        return sum;
    }
};


 二、K次取反后最大化的数组和

1005. K 次取反后最大化的数组和icon-default.png?t=N7T8https://.cn/problems/maximize-sum-of-array-after-k-negations/

题目描述 

        给你一个整数数组 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次。

class Solution {
public:
    int largestSumAfterKNegations(vector<int>& nums, int k) {
        for(int i=0;i<k;i++)
        {
            sort(nums.begin(),nums.end());
            nums[0]*=-1;
        }
        int result=0;
        for (int i=0;i<nums.size();i++)
        {
            result+=nums[i];
        }
        return result;
    }
};

        虽然但是这篇文章学的是贪心思想啊我们还是要用贪心来写的

贪心思想解法

        贪心的思路局部最优让绝对值大的负数变为正数当前数值达到最大整体最优整个数组和达到最大。局部最优可以推出全局最优。

  • 第一步将数组按照绝对值大小从大到小排序注意要按照绝对值的大小
  • 第二步从前向后遍历遇到负数将其变为正数同时K--
  • 第三步如果K还大于0那么反复转变数值最小的元素将K用完
  • 第四步求和
class Solution {
static bool cmp(int a, int b) {
    return abs(a) > abs(b);
}
public:
    int largestSumAfterKNegations(vector<int>& A, int K) {
        sort(A.begin(), A.end(), cmp);       // 第一步
        for (int i = 0; i < A.size(); i++) { // 第二步
            if (A[i] < 0 && K > 0) {
                A[i] *= -1;
                K--;
            }
        }
        if (K % 2 == 1) A[A.size() - 1] *= -1; // 第三步
        int result = 0;
        for (int a : A) result += a;        // 第四步
        return result;
    }
};


三、柠檬水找零

860. 柠檬水找零icon-default.png?t=N7T8https://.cn/problems/lemonade-change/

 题目描述 

        在柠檬水摊上每一杯柠檬水的售价为 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 

本题思路 

只需要维护三种金额的数量510和20。

有如下三种情况

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

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

class Solution {
public:
int five;
int ten;
int twenty;
public:
    bool lemonadeChange(vector<int>& bills) {
        for(int bill:bills)
        {
            if(bill==5)//情况1
            {
                five++;
            }

            if(bill==10)//情况2
            {
                if (five <= 0) return false;
                ten++;
                five--;
            }

            if(bill==20)//情况3
            {
                if(ten>0&&five>0)
                {
                    ten--;
                    five--;
                }else if(five>=3)
                {
                    five-=3;
                }else
                {
                    return false;
                }
            }
        }
        return true;
    }
};


                感谢你耐心的看到这里ღ( ´ᴗ` )比心如有哪里有错误请踢一脚作者o(╥﹏╥)o  

                                 

                                                                 给个三连再走嘛~      

阿里云国内75折 回扣 微信号:monov8
阿里云国际,腾讯云国际,低至75折。AWS 93折 免费开户实名账号 代冲值 优惠多多 微信号:monov8 飞机:@monov6