猫星球分发鱼干问题解析 | 豆包MarsCode AI刷题

63 阅读6分钟

学习记录:猫星球分发鱼干问题解析与实现

问题背景与描述

在猫星球的某一天,小R面临一个棘手的任务:给一排排队的猫分发鱼干。猫星球的居民以猫为主,每只猫都有自己的等级,等级越高的猫需要获得更多的鱼干。为了公平起见,小R需要按照以下规则进行分配:

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

小R的目标是通过满足上述规则,找到分发鱼干的最小总量。

这个问题表面上看是一个简单的分配问题,但实际上它隐含了两个方向的约束(左到右和右到左),需要综合考虑每只猫的等级与其相邻猫之间的关系。通过分析这个问题,我们发现这是一个局部递增与递减约束的优化问题,需要对每只猫在两个方向上满足约束。我们可以学到如何在复杂约束条件下优化资源分配。


问题分析

为了实现公平分配,我们可以将问题拆分成两个阶段:

  1. 确保从左向右满足规则:即如果当前猫的等级高于前一只猫,当前猫的鱼干数量应比前一只猫多。
  2. 确保从右向左满足规则:即如果当前猫的等级高于后一只猫,当前猫的鱼干数量应比后一只猫多。

最终通过两次遍历,找到满足双向约束的最小鱼干总量。

从算法上看,这是一个典型的动态规划问题,两次遍历分别解决不同方向的约束,并通过逐步调整优化分配。


算法思路

1. 初始化

所有猫最开始都分到一斤鱼干,这是满足规则的最低要求。我们使用一个数组 fish_count 记录每只猫的鱼干数量,初始状态为 [1, 1, ..., 1]

2. 第一次遍历:从左到右

从左到右遍历猫的等级数组 cats_levels,依次比较每只猫与前一只猫的等级关系:

  • 如果当前猫的等级 cats_levels[i] 高于前一只猫 cats_levels[i-1],则当前猫的鱼干数量 fish_count[i] 应比前一只猫多 1。
  • 否则,保持不变。

此遍历的目的是确保从左向右的等级约束得到满足。

3. 第二次遍历:从右到左

从右到左遍历数组 cats_levels,依次比较每只猫与后一只猫的等级关系:

  • 如果当前猫的等级 cats_levels[i] 高于后一只猫 cats_levels[i+1],则当前猫的鱼干数量 fish_count[i] 应至少比后一只猫多 1。

  • 为避免覆盖之前的结果,需取两次遍历计算值的最大值,即:

    fish_count[i] = max(fish_count[i], fish_count[i+1] + 1)
    

此遍历的目的是确保从右向左的等级约束得到满足,同时兼顾第一次遍历的结果。

4. 计算总鱼干

最后,将数组 fish_count 中的所有值累加,得到满足规则的最小鱼干总量。 这个问题可以通过动态规划思想分两次遍历解决:

  1. 第一次遍历(从左向右) :确保每只猫的鱼干满足它与前一只猫的等级差异要求。
  2. 第二次遍历(从右向左) :进一步调整鱼干数量,确保每只猫与后一只猫的等级差异也得到满足。

最后,将所有猫的鱼干数量相加,得到所需的最小总数。


示例与分析

示例 1

输入:n = 3, cats_levels = [1, 2, 2]

  1. 初始状态:每只猫至少得到 1 斤鱼干,fish_count = [1, 1, 1]

  2. 第一次遍历(从左到右):

    • cats_levels[1] > cats_levels[0]fish_count[1] = fish_count[0] + 1 = 2
    • cats_levels[2] <= cats_levels[1],保持不变。
    • 状态更新为:fish_count = [1, 2, 1]
  3. 第二次遍历(从右到左):

    • cats_levels[1] > cats_levels[2] 不成立,保持不变。
    • 状态保持为:fish_count = [1, 2, 1]
  4. 总鱼干数:1 + 2 + 1 = 4


代码详解

def solution(n, cats_levels):
    # 初始化每只猫的鱼干数量,每只猫至少得到一斤鱼干
    fish_count = [1] * n
    
    # 第一次遍历,确保每只猫的鱼干数量满足等级差异的要求
    for i in range(1, n):
        if cats_levels[i] > cats_levels[i - 1]:
            # 如果当前猫的等级高于前一只猫,增加鱼干数量
            fish_count[i] = fish_count[i - 1] + 1
    
    # 第二次遍历,确保每只猫的鱼干数量满足等级差异的要求
    for i in range(n - 2, -1, -1):
        if cats_levels[i] > cats_levels[i + 1]:
            # 如果当前猫的等级高于后一只猫,增加鱼干数量
            fish_count[i] = max(fish_count[i], fish_count[i + 1] + 1)
    
    # 计算总的鱼干数量
    total_fish = sum(fish_count)
    
    return total_fish

if __name__ == "__main__":
    # 你可以添加更多测试用例
    cats_levels1 = [1, 2, 2]
    cats_levels2 = [6, 5, 4, 3, 2, 16]
    cats_levels3 = [1, 2, 2, 3, 3, 20, 1, 2, 3, 3, 2, 1, 5, 6, 6, 5, 5, 7, 7, 4]
    print(solution(3, cats_levels1) == 4)
    print(solution(6, cats_levels2) == 17)
    print(solution(20, cats_levels3) == 35)

时间复杂度与空间复杂度

  • 时间复杂度
    两次遍历各需 O(n)O(n),总时间复杂度为 O(n)O(n)
  • 空间复杂度
    额外使用了 fish_count 数组,空间复杂度为 O(n)O(n)

学习心得与总结

通过解决这个问题,我有以下收获:

  1. 两次遍历法的高效性
    在资源分配和约束优化问题中,逐步调整是一个重要技巧。通过两次遍历,分别从两个方向处理约束条件,可以有效减少复杂度。
  2. 动态规划思想的应用
    本问题虽然未明确使用动态规划表,但通过记录中间状态(fish_count),逐步优化结果的过程与动态规划思想类似。这提醒我在解决实际问题时,动态规划的思想往往可以灵活应用。
  3. 边界条件的重要性
    通过增加多个测试用例(如等级完全相等或大幅波动),确保算法对各种边界情况都能正确处理。例如,当猫的等级完全相等时,每只猫应分到相同数量的鱼干。
  4. 代码调试与优化
    初次实现时容易忽略双向遍历的结合点,比如在第二次遍历中不取最大值可能导致错误结果。通过调试,我理解了如何在更新过程中保留较优解。

总之,本问题是一道经典的资源分配问题,通过细致的分析和高效的实现方法,我进一步提升了对动态规划和双向遍历算法的理解,也学到了如何在约束优化问题中找到全局最优解。