Skip to content

贪心与思维(十)

About 3774 wordsAbout 13 min

algochat-algo

2024-02-11

刚才我问DS,算法太难了,我们还有必要学算法吗?我们该怎么办?

DS说,我们还是有必要学,但是都需要3-6个月,好难呀。

贪心与思维(基本贪心策略/反悔/区间/字典序/数学/思维/脑筋急转弯/构造)

一、贪心算法定义

说白了,贪心算法可以理解为一种特殊的动态规划问题,拥有一些更特殊的性质,可以进一步降低动态规划算法的时间复杂度。 -- labuladong

贪心算法之区间调度问题

最优类问题,贪心算法是求的局部最优解。将求解过程分成若干个步骤,每个步骤都用贪心原则,选取最优的选择。

1.1、贪心算法能够解决那些问题呢?

  • 硬币找零问题:在某些硬币组合下,贪心算法总是可以得到最优解
  • 区间调度问题:假设你有一些任务,每个任务在一段时间内进行,你的目标是完成尽可能多的任务。
  • 分数背包问题:给定一组物品和一个载重量,你的目标是选择一组物品,使得总重量不超过载重量,且总价值最大
  • 股票买卖问题:
  • 霍夫曼编码:无损压缩数据的贪心算法。
  • Dijkstra算法:它是一种解决给定源顶点到其余各顶点拿的最短路径问题的贪心算法。

15.1 贪心算法

二、贪心算法学习之路

直接码上灵神的题单:分享丨【题单】贪心算法(基本贪心策略/反悔/区间/字典序/数学/思维/构造)

开始学习ing!!!

贪心策略:

有两种基本贪心策略:

  • 从最小/最大开始贪心,优先考虑最小/最大的数,从小到大/从大到小贪心。在此基础上,衍生出反悔贪心。
  • 从最左/最优开始贪心,思考第一个数/最后一个数的贪心策略,把n个数的原问题转换成n-1个数(或更少)的字问题。

2.1、从最小/最大开始贪心

3074. 重新分装苹果(20250228)

简单理解题目:给出整数apple数组,表示需要转运的苹果数,给出整数capacity, capacity[i]表示第i个包裹最多可以存放i个苹果,问将apple数组里面的苹果转运使用capacity存放,最少需要多少个包裹。

需要求最少的包裹,肯定是先用最大的包裹来存放,然后依次往后选择。

题目说,同一个包裹中的苹果可以分装到不同的箱子中。那么按照容量从大到小选择箱子装苹果,直到所有的苹果均装入箱子为止。

注意题目保证可以将包裹中的苹果重新分装到箱子中,就是说sum(apple) >= sum(capacity)

Python
class Solution:
    def minimumBoxes(self, apple: List[int], capacity: List[int]) -> int:
        total = sum(apple)
        capacity.sort(reverse=True)
        
        ans = 0 
        for x in capacity:
            if total <= 0:
                break
            total -= x
            ans += 1
        
        return ans

2279. 装满石头的背包的最大数量(20250228)

Python

1338. 数组大小减半(20250305)

  • 用哈希表(或者数组)统计每个元素的出现次数
  • 把出现次数从大到小排序,得到cnt数组
  • 遍历cnt,计算前缀和s,直到 s >= (n/2)为止,返回此时的下标加一就是答案。
Java

1710. 卡车上的最大单元数(20250305)

看了半天题都没看懂,最后总算是理解了。

boxTypes[i][0]表示i类箱子有多少个,最多只能装boxTypes[i][0]个

Java

2587. 重排数组以得到最大前缀分数(20250305)

Java

2.2、单序列配对

2144. 打折购买糖果的最小开销(20250305)

Java
class Solution {
    public int minimumCost(int[] cost) {
        Arrays.sort(cost);
        int ans = 0;
        for (int i = cost.length - 1; i >= 0; i -= 3) {
            ans += cost[i];
            if (i > 0) {
                ans += cost[i - 1];
            }
        }
        return ans;
    }
}

1877. 数组中最大数对和的最小值(20250310更新)

Java

881. 救生艇(20250310更新)

Java

2592. 最大化数组的伟大值(20250312更新)

先排序,就是遍历元素,找到第一个比它大的元素,(双指针)

Java
class Solution {
    public int maximizeGreatness(int[] nums) {
        // 田忌赛马
        Arrays.sort(nums);
        int i = 0;
        for (int x : nums) {
            if (x > nums[i]) {
                i++;
            }
        }
        return i;
    }
}

2576. 求出最多标记下标(20250312)

题目的核心就是求 2 * nums[i] <= nums[j],如果升序排序的话,那么i肯定小于j,我们想要的就是尽可能多的满足这个条件,所以当nums[i]比较小的时候,找到满足条件的最小的nums[j],(nums[j]右侧的数也是满足的),这个思路没什么问题。

但是在做的时候,没办法实现定位到满足条件的最小下标。用(n+1) / 2 来定位。

Java
class Solution {
    public int maxNumOfMarkedIndices(int[] nums) {
        Arrays.sort(nums);
        int n = nums.length;
        int i = 0;
        for (int x = (n + 1) / 2; x < n; x++) {
            if (nums[i] * 2 <= nums[x]) {
                i++;
            }
        }
        return i * 2;
    }
}

2.3、双序列配对

2037. 使每位学生都有座位的最少移动次数(20250313更新)

Java
class Solution {
    public int minMovesToSeat(int[] seats, int[] students) {
        Arrays.sort(seats);
        Arrays.sort(students);

        int n = seats.length;
        int ans = 0;
        for (int i = 0; i < n; i++) {
            ans += Math.abs(seats[i] - students[i]);
        }
        return ans;
    }
}

455. 分发饼干(20250313更新)

排序之后,开始想的是从0开始到最小的数组长度,一个g对应一个s,下标也一一对应,但是这个是错误的。

我们应该是找到比g[i]大的最小的s[j]的值,这样才能尽可能多的满足小孩。

Java

870. 优势洗牌(20250312更新)

田忌赛马

Java

826. 安排工作以达到最大收益(20250314更新)

题目还是很容易理解的,主要是解决遇到的问题

  • 到底是先遍历worker还是先遍历difficulty? (遍历worker)
  • 大于了不能直接取,还需要往后判断,取最大值,解决不了取到满足条件的最大的profit,就是当worker[i] = 6的时候,取不到最大的profit = 30(在循环里面使用while来实现获取最大的profit)
Java

2.4、从最左/最右开始贪心

对于无法排序的题目,尝试从左到右/从右到左贪心。思考第一个数/最后一个数的贪心策略,把n个数的原问题转换成n-1个数(或者更少)的字问题。

读者可以对比动态规划中线性DP、状态机DP的区别,思考什么情况下只能DP不能贪心,从而加深对局部最优和全局最优的理解。

3402. 使每一列严格递增的最少操作次数(20250314更新)

Java

3191. 使二进制数组全部等于 1 的最少操作次数 I(20250318更新)

Java
class Solution {
    public int minOperations(int[] nums) {
        int ans = 0;
        int n = nums.length;
        for (int i = 0; i < n - 2; i++) {
            if (nums[i] == 0) {
                nums[i + 1] ^= 1;
                nums[i + 2] ^= 1;
                ans++;
            }
        }
        return nums[n - 2] == 1 && nums[n - 1] == 1 ? ans : -1;
    }
}

Changelog

Last Updated: View All Changelog
  • feat(wiki): algo: 算法总结

    On 3/30/25

求求了,快滚去学习!!!

求求了求求了,快去学习吧!

【题单】贪心算法

不知道方向的时候,可以多看看书,书会给你指明下一步该干什么,加油!