每日一水(贪心算法) | 豆包MarsCode AI刷题

122 阅读4分钟

引入

贪心算法

贪心算法是一种算法设计策略,旨在通过在每一步选择中都采取当前状态下的最佳选择,从而期望在整体上达到最佳解。它的基本思想是:在解决一个问题时,总是做出在当前看起来最优的选择,而不考虑可能的后果。

贪心算法的特点:

  1. 局部最优选择:在每一步选择时,选择当前看起来最好的选项。
  2. 无回溯:一旦做出选择,就不再改变之前的选择。
  3. 适用性:贪心算法适用于一些问题,但并不是所有问题都可以通过贪心算法得到最优解。

应用场景:

贪心算法常用于以下问题:

  • 最小生成树(如Kruskal和Prim算法)
  • 单源最短路径(如Dijkstra算法)
  • 活动选择问题
  • 背包问题(0/1背包问题通常不适用贪心算法,但分数背包问题可以)
  • Huffman编码

实现步骤:

实现贪心算法的步骤通常包括:

  1. 问题定义:明确要解决的问题和目标。
  2. 选择标准:确定在每一步选择的标准,即在当前状态下如何选择“最优解”。
  3. 构造解:通过重复选择局部最优解,构造出一个全局解。
  4. 验证:检查最终得到的解是否满足需要的条件,若满足则返回结果,若不满足则可能需要重新审视选择标准或解法。

今天讲解几道水题来帮助同学们和自己更好理解这个算法

题目一

先水一道

小C发现了一种特殊的数字,称为类二进制数字,即仅由数字0和1组成的十进制数。例如,1011100都是类二进制数字,而1123001则不是。现在,小C手上有一个正整数n,他想知道最少需要多少个类二进制数字相加才能得到n

解析

这题应该很好理解贪心原理,就是两个类二进制数字组成一个十进制数字,类二进制数字的一个位只能是01, 那么求最少需要多少个类二进制数字相加就只考虑十进制数字最大数的位对应的类二进制数字的位是1

解题思路就是先遍历十进制数找出最大数,该最大数就是最少需要的类二进制数.

代码如下

int solution(string n) {
    int length = n.length();
    int result = 0;
    for (int i = 0; i < length; i++) {
        int digit = n[i] - '0';
        if (digit >= result) {
            result = digit;
        }
    }
    return result;
}

题目二

在猫星球上,小R负责给一行排队的猫分发鱼干。每只猫有一个等级,等级越高的猫应该得到更多的鱼干。规则如下:

  1. 每只猫至少得到一斤鱼干。
  2. 如果一只猫的等级高于它相邻的猫,它就应该得到比相邻的猫更多的鱼干。

小R想知道,为了公平地满足所有猫的等级差异,他至少需要准备多少斤鱼干。

解析

也比较好理解以{6, 5, 4, 3, 2, 16}为例,每只猫咪得到的小鱼干数量是{5, 4, 3, 2, 1, 2}

解题思路如下

  1. 初始化:每只猫至少得到一斤鱼干,所以我们需要一个数组来记录每只猫得到的鱼干数量。

  2. 遍历比较:从左到右遍历猫的等级数组,比较当前猫和前一只猫的等级。

    • 如果当前猫的等级高于前一只猫,那么当前猫得到的鱼干应该比前一只猫多一斤。
    • 如果当前猫的等级等于前一只猫,那么当前猫得到的鱼干和前一只猫一样多。
    • 如果当前猫的等级低于前一只猫,那么当前猫得到的鱼干可以和前一只猫一样多,但需要检查后续的猫是否需要更多的鱼干。
  3. 调整鱼干数量:从右到左遍历猫的等级数组,比较当前猫和前一只猫的等级。

    • 如果当前猫的等级高于前一只猫,那么当前猫得到的鱼干应该是当前猫得到的鱼干数和前一只猫得到的鱼干数+1的最大值
  4. 计算总鱼干数量:最后,将所有猫得到的鱼干数量相加,得到总的最少鱼干数量。

代码如下

int solution(int n, std::vector<int> cats_levels) {
    using namespace std;
  // 初始化每只猫的鱼干数量为1
    vector<int> fish_amounts(n, 1);
    
    // 从左到右遍历,更新每只猫的鱼干数量
    for (int i = 1; i < n; ++i) {
        if (cats_levels[i] > cats_levels[i - 1]) {
            fish_amounts[i] = fish_amounts[i - 1] + 1;
        }
    }
    
    // 从右到左遍历,更新每只猫的鱼干数量
    for (int i = n - 2; i >= 0; --i) {
        if (cats_levels[i] > cats_levels[i + 1]) {
            fish_amounts[i] = max(fish_amounts[i], fish_amounts[i + 1] + 1);
            
        }
    }
    
    // 累加所有猫的鱼干数量
    int total_fish = accumulate(fish_amounts.begin(), fish_amounts.end(), 0);
    
    return total_fish;
}

你学废了吗?