九日集训(第六天)贪心

84 阅读6分钟

九日集训(第六天)贪心

零、昨日复盘

九日集训(第五天)排序

​ 今天是九日集训的第六天,我们来学习贪心的概念,视频版请 戳我

一、概念定义

​ 所谓贪心,总是做出在当前看来是最好的选择。也就是说,不从整体最优上进行考虑,算法得到的是在某种意义上的局部最优解。

​ 比如,对于一个全是正整数的数组,我要找到其中两个数,使得它们的乘积最大,毫无疑问,一定是取 最大次大 的两个数进行相乘,得到的结果最大。这个就是贪心思想。

​ 那么接下来,让我们做几道例题,来真正了解一下贪心。

二、题目分析

1、最大乘积差

img

  • (1) 先对数组进行排序;
  • (2) 利用贪心,直接取最大的两个数下标作为 w 和 x,最小的两个数下标作为 y 和 z。

2、三角形的最大周长

img

  • (1) 利用三角形两边之和大于第三边的性质,假设小的两条边为 a 和 b,大的那条为 c,那么必须满足如下等式才能满足它是一个三角形: a + b > c

​ 这个问题要求最大的三角形周长,那么势必是最大的那条边 c 越大越好,所以我们可以枚举将所有的边按照递增排序,然后逆序枚举最大的那条边 c,去剩下的边里找小的两条边,最好的情况肯定是比 c 小的最大和次大边最优,如果这两条边都不能满足上述不等式,剩下的边也就肯定也不满足了,所以只需要一个循环即可解决问题。

3、数组拆分 I

img

  • (1) 对数组进行排序;
  • (2) 选择偶数项进行相加,就是最大总和。

4、救生艇

img

  • (1) 按照重量从小到大排序;
  • (2) 如果只剩一个人,那么直接加上一只船,并且跳出循环;
  • (3) 如果最重的那个人和最轻的那个人加起来不能坐一条船,那么最重的那个人势必只能 "一意孤行" 了,因为其他人更加不可能和他同行。转变成 的子问题。
  • (4) 如果最重的那个人可以和最轻的人一起坐一条船,那就顺带捎上,转变成 的子问题。

5、摆动排序 II

img

  • (1) 这题要求没有返回值的原地排序,所以我们可以先将数组拷贝一份出来;
  • (2) 然后对拷贝的数组进行排序操作;
  • (3) 然后,不断进行插孔,先插奇数位置;
  • (4) 再插偶数位置;

6、分发饼干

img

  • (1) 分别对 胃口值 和 饼干 都进行递增排序;
  • (2) 然后将两个游标都指向 胃口值 和 饼干 的末尾;
  • (3) 如果某一个 饼干 和 胃口值 满足s[j] >= g[i]的条件,则两个游标都往前移动一格,并且答案加一;
  • (4) 否则,胃口值的游标往前移动一格,寻找更加容易满足条件的解,直到 饼干 或者 胃口值 枚举完毕;

7、最少操作使数组递增

img

  • (1) 如果 当前的数前一个数 大,则没有任何消耗,并且重置 前一个数当前数加一,进行下一轮判断;
  • (2) 否则,需要把 当前的数 变成 前一个数加一

8、有效三角形的个数

img

  • (1) 首先,将所有数组元素按照递增排序;
  • (2) 利用三个游标 i、j、k,递增枚举三条边;
  • (3) 如果遇到 nums[i] + nums[j] <= nums[k] 则直接跳出第三层循环,因为边是递增的,后面的 k 都会满足这个条件,从而无法构成三角形;
  • (4) 在 i 固定的情况下,满足条件的边数就是 (j, k) 二元组的对数(从循环的跳出条件来看 k 的取值是 j+1,j+2,j+3, ...,k-1,所以总的个数就是 (k-1)-(j+1) + 1 = k-j-1 );
  • (5) 继续自增 j,如果发现 j 和 k 相等,则自增 k; 总体来说,就是枚举 i 和 j, k 是跟着 j 一起走的,所以时间复杂度为 O(n*n)。

九日集训(第六天)贪心

三、课后习题

课后所有习题,在上文都能找到答案,所以务必将下面的题全部刷完。 第六天了,有什么感觉吗?最后四天,相信已经不用激励,你已经养成了习惯,自驱动吧! 坚持!加油!你可以的!

两个数对之间的最大乘积差 视频题解

三角形的最大周长

数组拆分

救生艇

摆动排序 II

分发饼干

最少操作使数组递增

使数组唯一的最小增量

有效三角形的个数

561. 数组拆分

给定长度为 2n 的整数数组 nums ,你的任务是将这些数分成 n 对, 例如 (a1, b1), (a2, b2), ..., (an, bn) ,使得从 1nmin(ai, bi) 总和最大。

返回该 最大总和

示例 1:

输入: nums = [1,4,3,2] 输出: 4 解释: 所有可能的分法(忽略元素顺序)为:

  1. (1, 4), (2, 3) -> min(1, 4) + min(2, 3) = 1 + 2 = 3
  2. (1, 3), (2, 4) -> min(1, 3) + min(2, 4) = 1 + 2 = 3
  3. (1, 2), (3, 4) -> min(1, 2) + min(3, 4) = 1 + 3 = 4 所以最大总和为 4

示例 2:

输入: nums = [6,2,6,5,1,2] 输出: 9 解释: 最优的分法为 (2, 1), (2, 5), (6, 6). min(2, 1) + min(2, 5) + min(6, 6) = 1 + 2 + 6 = 9

提示:

  • 1 <= n <= 10^4
  • nums.length == 2 * n
  • -10^4 <= nums[i] <= 10^4

代码

class Solution {
public:
    int arrayPairSum(vector<int>& nums) {
        sort(nums.begin(), nums.end());
        int n = nums.size();
        int ans = 0; // 初始化累加和 ans 为 0
        for (int i = 0; i < n; i += 2) {
            ans += nums[i];
        }
        return ans;
    }
};

这段代码的逻辑是通过先对数组 nums 进行排序,然后遍历排序后的数组,每次跳过一个元素(即以步长为2进行循环),将当前元素加到累加和 ans 中。这样做的目的是利用了一个性质:在已排序的数组中,每对相邻的元素构成的对 (ai, bi) 中的较小值总是在偶数索引(因为数组是从0开始索引的,所以这里的“偶数”是指实际的索引+1之后的偶数)。通过这种方法,可以保证加和的是每对中较小的那个,从而达到题目要求的“使得从 1nmin(ai, bi) 总和最大”。

简而言之,这个策略是基于“排序后的相邻成对挑选”的策略,保证了能够获得最大的 min(ai, bi) 总和。