青训营X豆包MarsCode 技术训练营第二题 | 豆包MarsCode AI 刷题

39 阅读4分钟

徒步旅行中的补给问题

问题描述

小R正在计划一次从地点A到地点B的徒步旅行,总路程需要 N 天。为了在旅途中保持充足的能量,小R每天必须消耗1份食物。幸运的是,小R在路途中每天都会经过一个补给站,可以购买食物进行补充。然而,每个补给站的食物每份的价格可能不同,并且小R最多只能同时携带 K 份食物。

现在,小R希望在保证每天都有食物的前提下,以最小的花费完成这次徒步旅行。你能帮助小R计算出最低的花费是多少吗?

解题思路

  1. 定义状态:我们可以使用一个数组 dp 来表示在第 i 天结束时,携带 j 份食物的最小花费。

  2. 状态转移:对于每一天,我们需要考虑在前一天结束时携带不同数量的食物,并计算在当前补给站购买食物的花费。

  3. 初始化:在第0天,我们没有食物,所以 dp[0][0] = 0,其他状态初始化为无穷大。

  4. 最终结果:我们需要找到在第 N 天结束时,携带 0 份食物的最小花费。

  5. public class Main { public static int solution(int n, int k, int[] data) { // 定义一个二维数组 dp,dp[i][j] 表示在第 i 天结束时,携带 j 份食物的最小花费 int[][] dp = new int[n + 1][k + 1];

    // 初始化 dp 数组,除了 dp[0][0] 为 0,其他都设为无穷大
    for (int i = 0; i <= n; i++) {
        for (int j = 0; j <= k; j++) {
            dp[i][j] = Integer.MAX_VALUE;
        }
    }
    dp[0][0] = 0;
    
    // 动态规划状态转移
    for (int i = 1; i <= n; i++) {
        for (int j = 0; j <= k; j++) {
            // 如果前一天结束时携带 j 份食物是可行的
            if (dp[i - 1][j] != Integer.MAX_VALUE) {
                // 不购买食物
                if (j > 0) {
                    dp[i][j - 1] = Math.min(dp[i][j - 1], dp[i - 1][j]);
                }
                // 购买食物
                for (int buy = 1; buy <= k - j; buy++) {
                    dp[i][j + buy - 1] = Math.min(dp[i][j + buy - 1], dp[i - 1][j] + buy * data[i - 1]);
                }
            }
        }
    }
    
    // 返回在第 n 天结束时,携带 0 份食物的最小花费
    return dp[n][0];
    

    }

    public static void main(String[] args) { // Add your test cases here System.out.println(solution(5, 2, new int[]{1, 2, 3, 3, 2}) == 9); } }

关键步骤解释

  1. 初始化:我们将 dp 数组初始化为无穷大,表示初始状态下不可能达到的状态。
  2. 状态转移:对于每一天,我们考虑在前一天结束时携带不同数量的食物,并计算在当前补给站购买食物的花费。
  3. 最终结果:我们在第 N 天结束时,携带 0 份食物的最小花费即为所求。 在徒步旅行中,小R需要从地点A到地点B,总路程需要 N 天。每天必须消耗1份食物,且每天经过一个补给站,可以购买食物。每个补给站的食物价格不同,且小R最多只能携带 K 份食物。目标是计算出完成旅行的最低花费。

动态规划的基本概念

动态规划(Dynamic Programming, DP)是一种通过将问题分解为子问题并存储子问题的解来解决复杂问题的方法。它通常用于优化问题,其中问题的解可以通过组合子问题的解来获得。

状态定义

在动态规划中,状态是指问题在某一阶段的特定情况。对于这个问题,我们可以定义状态 dp[i][j] 表示在第 i 天结束时,携带 j 份食物的最小花费。

状态转移方程

状态转移方程描述了如何从一个状态转移到另一个状态。对于这个问题,状态转移方程如下:

  • 如果前一天结束时携带 j 份食物是可行的(即 dp[i-1][j] 不是无穷大),则可以考虑以下两种情况:

    1. 不购买食物:如果 j > 0,则 dp[i][j-1] 可以从 dp[i-1][j] 转移过来。
    2. 购买食物:对于 buy 从 1 到 k-j,表示在当前补给站购买 buy 份食物,则 dp[i][j+buy-1] 可以从 dp[i-1][j] + buy * data[i-1] 转移过来。

初始化

初始化是动态规划中非常重要的一步。对于这个问题,我们需要将 dp 数组初始化为无穷大,表示初始状态下不可能达到的状态。只有 dp[0][0] 初始化为 0,表示在第0天结束时,没有食物且花费为0。

最终结果

最终结果是我们在第 N 天结束时,携带 0 份食物的最小花费,即 dp[N][0]。 通过动态规划,我们可以有效地解决这个问题。关键在于正确地定义状态和状态转移方程,并进行适当的初始化。动态规划的核心思想是将复杂问题分解为简单的子问题,并通过存储子问题的解来避免重复计算,从而提高效率。