刷题实践:补给站最优花费问题 | 豆包MarsCode AI刷题

133 阅读6分钟

问题描述

小U计划进行一场从地点A到地点B的徒步旅行,旅行总共需要 M 天。为了在旅途中确保安全,小U每天都需要消耗一份食物。在路程中,小U会经过一些补给站,这些补给站分布在不同的天数上,且每个补给站的食物价格各不相同。

小U需要在这些补给站中购买食物,以确保每天都有足够的食物。现在她想知道,如何规划在不同补给站的购买策略,以使她能够花费最少的钱顺利完成这次旅行。

  • M:总路程所需的天数。
  • N:路上补给站的数量。
  • p:每个补给站的描述,包含两个数字 A 和 B,表示第 A 天有一个补给站,并且该站每份食物的价格为 B 元。

保证第0天一定有一个补给站,并且补给站是按顺序出现的。

测试样例

示例一:

输入:m = 5 ,n = 4 ,p = [[0, 2], [1, 3], [2, 1], [3, 2]]

输出:7

示例二:

输入:m = 6 ,n = 5 ,p = [[0, 1], [1, 5], [2, 2], [3, 4], [5, 1]]

输出:6

示例三:

输入:m = 4 ,n = 3 ,p = [[0, 3], [2, 2], [3, 1]]

输出:9

解题思路

这个问题是一个典型的动态规划问题,目标是在给定的补给站条件下,找到完成旅行的最小花费。我们可以按照以下步骤来解决这个问题:

  1. 初始化DP数组:创建一个长度为 m+1 的数组 dp,其中 dp[i] 表示到达第 i 天的最小花费。初始化所有元素为无穷大,除了 dp[0] 设置为0,因为第0天有一个补给站,不需要额外花费。

  2. 遍历补给站:对于每个补给站,我们获取其位置 day 和食物价格 price。然后,我们更新到达该补给站的最小花费 dp[day]

  3. 更新后续天数的花费:从当前补给站开始,更新到达后续每一天的最小花费。这意味着,如果我们在第 day 天补给,那么到达第 j 天(j > day)的最小花费可以是 dp[day] + price

  4. 返回结果:最后,dp[m] 将存储到达第 m 天的最小花费,这是我们要求的结果。

核心代码

def solution(m: int, n: int, p: list[list[int]]) -> int:
    # 初始化 dp 数组
    dp = [float('inf')] * (m + 1)
    dp[0] = 0  # 第 0 天有一个补给站
    
    for i in range(n):
        day, price = p[i]
        # 更新到达第 day 天的最小花费
        dp[day] = min(dp[day], price)
        # 从当前补给站开始,更新到达后续每一天的最小花费
        for j in range(day + 1, m + 1):
            dp[j] = min(dp[j], dp[day] + price)
    
    # 返回到达第 m 天的最小花费
    return dp[m]



if __name__ == "__main__":
    # Add your test cases here

    print(solution(5, 4, [[0, 2], [1, 3], [2, 1], [3, 2]]) == 7)

核心知识

  1. 动态规划(DP)的基本概念: 动态规划是一种解决复杂问题的方法,通常将问题分解为更简单的子问题,并通过解决这些子问题来构建原始问题的解。在本题中,我们通过构建一个一维数组 dp 来存储到达每一天的最小花费,从而避免了重复计算。

  2. 状态转移方程的构建: 状态转移方程是动态规划的核心,它定义了如何基于已知的子问题解来构建当前问题的解。在本题中,状态转移方程 dp[j] = min(dp[j], dp[day] + price) 表示,如果我们在第 day 天补给,那么到达第 j 天的最小花费可以是第 day 天的最小花费加上补给站的价格。这个方程允许我们考虑所有可能的补给策略,并选择成本最低的一个。

  3. 初始化和边界条件的处理: 在动态规划中,正确的初始化和边界条件处理至关重要。在本题中,我们初始化 dp[0] = 0,因为第0天有一个补给站,不需要额外花费。其他天数初始化为无穷大,表示在没有补给站的情况下,我们假设到达那天的花费是无穷大。这样,当我们更新 dp 数组时,只有当补给站的价格加上之前天数的最小花费小于当前的 dp[j] 时,才会进行更新。

  4. 循环结构和迭代逻辑: 在实现动态规划时,循环结构和迭代逻辑的设计对于算法的效率和正确性至关重要。在本题中,我们首先遍历所有补给站,然后在每个补给站的基础上,迭代更新从该补给站到最后一天的所有天数的最小花费。这种自底向上的迭代方法确保了每个状态在被使用之前都已经被计算和更新。

  5. 空间优化: 在某些情况下,动态规划问题可以通过空间优化来减少内存使用。在本题中,由于我们只关心到达每一天的最小花费,而不需要存储整个 dp 数组的历史信息,因此可以考虑使用一个一维数组来交替存储当前和上一个状态的值,从而将空间复杂度从 O(m) 降低到 O(1)。

  6. 算法的正确性验证: 在实现动态规划算法后,通过构造测试案例来验证算法的正确性是非常重要的。在本题中,我们可以通过比较不同补给站策略的花费,确保算法能够正确地选择成本最低的策略。

总结

  1. 理解问题:首先,我们需要理解问题的要求,即在给定的补给站条件下,找到完成旅行的最小花费。

  2. 识别DP结构:识别出问题的动态规划结构,确定状态(dp[i])和状态转移方程。

  3. 实现DP逻辑:根据状态转移方程,实现动态规划逻辑,注意循环的顺序和条件,以避免错误地更新状态。

  4. 测试和验证:通过添加测试案例来验证代码的正确性,确保所有逻辑都被正确实现。

  5. 优化思考:在实现基本功能后,考虑是否有进一步优化的空间,比如减少不必要的计算或改进算法效率。

  6. 代码清晰性:在编写代码时,保持代码的可读性和清晰性,使用清晰的变量名和注释来解释关键步骤。

使用豆包marscode AI的优势

  1. 快速搭建代码框架:MarsCode AI能够根据算法题干描述快速生成代码框架,帮助开发者节省编写初始代码的时间,特别是对于初学者或面对复杂问题时,这样的辅助可以加速开发进程。

  2. 智能代码补全:在编写代码的过程中,MarsCode AI可以提供智能的代码补全建议,减少编码错误,提高编码效率。

  3. 实时错误检测与修复建议:MarsCode AI具备错误检测能力,能够在代码编写过程中及时发现潜在问题,并提供修改建议,有助于减少调试时间,确保代码质量。

  4. 优化建议:AI可以分析已有的代码,提供优化建议,包括减少时间复杂度、优化空间使用、提升代码可读性等方面,帮助开发者编写更高效、优雅的代码。