第八章 贪心算法 part01

发布于:2024-07-23 ⋅ 阅读:(125) ⋅ 点赞:(0)

分发饼干

题目描述

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

对每个孩子 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 和饼干的大小数组 s 进行排序。这样做的目的是为了能够从需求最大和大小最大的饼干开始匹配,以尽可能满足更多的孩子。
  2. 从大到小匹配:从需求最大的孩子开始,尝试用最大的饼干去满足他。如果当前最大的饼干可以满足当前孩子的需求,则将该饼干分配给该孩子,并移动到下一个更大的饼干和下一个需求更大的孩子。
  3. 贪心选择:每次选择当前最大的饼干去满足当前需求最大的孩子,这样可以保证每次选择都是局部最优的,从而达到全局最优的结果。

代码实现

测试地址:https://leetcode.cn/problems/assign-cookies/

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;
        // 初始化满足的孩子数量为0
        int count = 0;
    
        // 从需求最大的孩子开始遍历
        for (int i = g.size() - 1; i >= 0; i--) {
            // 如果当前饼干可以满足当前孩子的需求
            if (index >= 0 && s[index] >= g[i]) {
                // 满足的孩子数量加一
                count++;
                // 使用过的饼干索引减一
                index--;
            }
        }
    
        // 返回满足的孩子数量
        return count;
    }
};

摆动序列

题目描述

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

  • 例如, [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

解题思路

贪心思路:每次选择形成摆动的相邻元素对,这样可以保证每次选择都是局部最优的,从而达到全局最优的结果。

本题共有三种特殊情况需要处理:

  • 上下坡中有平坡

    • 当数组中存在连续的相同值时,curdiff 会等于0,此时平坡的长度不会被计入摆动序列的长度中,此时峰值记录的条件为:(preDiff <= 0 && curDiff > 0) || (preDiff >= 0 && curDiff < 0)

      image

  • 数组首尾两端

    • 当数组元素为2时,为了匹配上面的条件,可以在虚拟一个节点作为作为第一个节点的平坡,然后将count的值初始化为1,因为第一个节点到第二个节点肯定会有一个峰值,从而匹配情况一种提到的:prediff=0 && cur>0的情况,count++,摆动序列值为2

      image

  • 单调中有平坡

    • 如果是从上述条件中推理,在下面这个情况中是会有3个峰值的,但是实际摆动序列为2,造成这个情况的原因是因为我们实时的更新了 prediff的值,而不是当curdiff 的符号与 prediff 的符号相反时,再进行count++以及更新prediff的操作。

    image

代码实现

测试地址:https://leetcode.cn/problems/wiggle-subsequence/

class Solution {
public:
    int wiggleMaxLength(vector<int> &nums) {
        if (nums.size() == 1)
            return 1; // 如果数组只有一个元素,则摆动序列的长度为1
    
        int count = 1; // 初始化摆动序列的长度为1
        int prediff = 0; // 初始化前一个差值为0
        int curdiff = 0; // 初始化当前差值为0

        for (int i = 0; i < nums.size() - 1; i++) {
            curdiff = nums[i + 1] - nums[i]; // 计算当前差值
            // 如果当前差值和前一个差值形成摆动,则更新摆动序列的长度
            if ((prediff <= 0 && curdiff > 0) || (prediff >= 0 && curdiff < 0)) {
                count++;
                prediff = curdiff; // 更新前一个差值
            }
        }
        return count; // 返回摆动序列的长度
    }
};

最大子数组和

题目描述

给你一个整数数组 nums ,请你找出一个具有最大和的连续子数组(子数组最少包含一个元素),返回其最大和。

子数组 是数组中的一个连续部分。

示例 1:

输入:nums = [-2,1,-3,4,-1,2,1,-5,4]
输出:6
解释:连续子数组 [4,-1,2,1] 的和最大,为 6 。

示例 2:

输入:nums = [1]
输出:1

示例 3:

输入:nums = [5,4,-1,7,8]
输出:23

解题思路

  1. 初始化:首先初始化最大子序列和 subSum 为最小整数值,确保任何子数组的和都能够更新这个值。同时,初始化当前计算的子序列和 tmpSum 为0。
  2. 遍历数组:遍历数组中的每一个元素,对 tmpSum 进行累加,计算得到当前的子序列和。
  3. 更新最大子序列和:如果当前子序列和 tmpSum 大于已知的最大子序列和 subSum,那么就更新最大子序列和为当前子序列和。
  4. 贪心选择:如果当前子序列和 tmpSum 小于等于0,那么就抛弃之前的子序列,因为这些子序列和对于后续的和来说,只会减小总的和,不会增加。因此我们选择从下一个位置重新开始计算子序列和。

代码实现

测试地址:https://leetcode.cn/problems/maximum-subarray/

class Solution {
public:
    int maxSubArray(vector<int>& nums) {
        int subSum = INT_MIN; // 初始化最大子序列和为最小整数值
        int tmpSum = 0; // 初始化当前计算的子序列和为0
        for (int i = 0; i < nums.size(); i++) {
            tmpSum += nums[i]; // 计算当前子序列和
            if (tmpSum > subSum) 
                subSum = tmpSum; // 如果当前子序列和大于最大子序列和,则更新最大子序列和
            if (tmpSum <= 0)
                tmpSum = 0; // 如果当前子序列和小于等于0,那么抛弃之前的子序列,从新的位置重新计算子序列和
        }
        return subSum; // 返回最大子序列和
    }
};

网站公告

今日签到

点亮在社区的每一天
去签到