AI 刷题 56.贪心猫的鱼干大分配 |豆包MarsCode AI刷题

145 阅读4分钟

贪心猫的鱼干大分配

题目描述

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

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

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

题目分析

正如题目所示,这是一道典型的贪心算法题。要求每只猫至少获得一条鱼干,如果相邻的猫等级高就应该得到更多鱼干。这种类似的题目很多,都是换汤不换药,一旦做出一道该类型题目,这类题就算是掌握了。但是这种类型的题目思路还是有点难想的,接下来我将带着读者一同找出本体破冰点。

我们从题目知道问的是至少准备多少斤鱼干,所以就是求最少鱼干,那么在遵守题目规则的情况下,相邻小猫获得鱼干期望最小差值应为1。我们先考虑递增的情况,即如下:

小猫等级:【1,3,4,6,12】

小猫鱼干:【1,2,3,4,5】

在这种情况下可以用最少鱼干完成分配。现在让我们加入特殊情况

情况1:

小猫等级:【1,4,6,12,2,1】

小猫鱼干:【1,2,3,4,2,1】

情况2:

小猫等级:【1,3,4,6,6,12】

小猫鱼干:【1,2,3,4,1,2】

显而易见,当出现这两种情况时,小猫鱼干分配不再遵从常规顺序递增情况。那么我们该如何处理特殊情况呢?

如果我们将小猫进行拆分,可以发现他们的数值其实也遵从局部单调函数的分布,具体如下图:

D1AAFFD5672FD9ECB75D34EE581ACDF4.png

现在我们基于函数图形对数据分析,从第一个递增函数起点开始初始化为1,如果相邻小猫级别大于他,数量便加1,如果相邻小猫数量小于或等于他,代表目前不处在递增函数里面,所以将当前结点视为递增函数新起点,重新将起始点初始化为1,然后重复操作。可能说的有点抽象,那么接下来请看具体代码实现

代码展示

    for (int i = 1; i < n; i++) {
        if (levels[i] > levels[i - 1]) {
            fishCount[i] = fishCount[i - 1] + 1;
        }
    }
   

考虑完递增情况,接下来介绍递减情况。递减时的函数形状可以视作小猫逆序情况下的递增函数,所以我们可以用相同的处理方法将其处理,那么接下来请看具体代码实现

代码展示

    for (int i = n - 2; i >= 0; i--) {
        if (levels[i] > levels[i + 1]) {
            fishCount[i] = Math.max(fishCount[i], fishCount[i + 1] + 1);
        }
    }

当情况考虑完,这道题也就做出来了。可以发现这道题将情况考虑出来后其实并不难,难在你要发现这两种情况。就这道题而言你需要画图分析,所以这道题还是很有学习价值的。现在整道题讲解完毕,下面是完整代码

完整代码展示

import java.util.ArrayList;
import java.util.List;
public class Main {
public static int solution(int n, List<Integer> cats_levels) {
    int[] levels = cats_levels.stream().mapToInt(i -> i).toArray();
    int[] fishCount = new int[n];

    // 初始化每只猫至少获得一斤鱼干
    for (int i = 0; i < n; i++) {
        fishCount[i] = 1;
    }

    // 从左到右遍历
    for (int i = 1; i < n; i++) {
        if (levels[i] > levels[i - 1]) {
            fishCount[i] = fishCount[i - 1] + 1;
        }
    }

    // 从右到左遍历
    for (int i = n - 2; i >= 0; i--) {
        if (levels[i] > levels[i + 1]) {
            fishCount[i] = Math.max(fishCount[i], fishCount[i + 1] + 1);
        }
    }

    // 计算总的鱼干数量
    int sum = 0;
    for (int fish : fishCount) {
        sum += fish;
    }

    return sum;
}

public static void main(String[] args) {
    List<Integer> catsLevels1 = new ArrayList<>();
    catsLevels1.add(1);
    catsLevels1.add(2);
    catsLevels1.add(2);

    List<Integer> catsLevels2 = new ArrayList<>();
    catsLevels2.add(6);
    catsLevels2.add(5);
    catsLevels2.add(4);
    catsLevels2.add(3);
    catsLevels2.add(2);
    catsLevels2.add(16);

    List<Integer> catsLevels3 = new ArrayList<>();
    catsLevels3.add(1);
    catsLevels3.add(2);
    catsLevels3.add(2);
    catsLevels3.add(3);
    catsLevels3.add(3);
    catsLevels3.add(20);
    catsLevels3.add(1);
    catsLevels3.add(2);
    catsLevels3.add(3);
    catsLevels3.add(3);
    catsLevels3.add(2);
    catsLevels3.add(1);
    catsLevels3.add(5);
    catsLevels3.add(6);
    catsLevels3.add(6);
    catsLevels3.add(5);
    catsLevels3.add(5);
    catsLevels3.add(7);
    catsLevels3.add(7);
    catsLevels3.add(4);

    System.out.println(solution(3, catsLevels1) == 4); // 应输出 true
    System.out.println(solution(6, catsLevels2) == 17); // 应输出 true
    System.out.println(solution(20, catsLevels3) == 35); // 应输出 true
}
}

收获与总结

这道题是一道非常经典的贪心算法题,难度和实现代码量都适中,很适合初学者练手。我讲解这道题的原因在于我见过很多类似的题目,他们的核心思路几乎没有变化,当完成他们中的一道题后,其他的题也就顺理成章的可以实现了。总的来说这道题的学习意义大于提升意义,对于大佬来说,我的这篇文章有点浪费时间,但对于代码基础不是那么强的,我相信这道题可以教会你遇到类似的题应该如何思考,如何快速作出反应。以上分析仅为个人观点,如有不满,敬请开喷。