徒步旅行中的补给问题 js

146 阅读3分钟

问题描述

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

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

**输入 **

  • n 总路程需要的天数
  • k 小R最多能同时携带食物的份数
  • data[i] 第i天补给站每份食物的价格

**输出 **

  • 返回完成这次徒步旅行的最小花费

**约束条件 **

  • 1 < n,k < 1000
  • 1 < data[i] < 10000

测试样例

样例1:

输入:n = 5 ,k = 2 ,data = [1, 2, 3, 3, 2]
输出:9

样例2:

输入:n = 6 ,k = 3 ,data = [4, 1, 5, 2, 1, 3]
输出:9

样例3:

输入:n = 4 ,k = 1 ,data = [3, 2, 4, 1]
输出:10

function solution(n, k, data) {
    let len = data.length
    let dp = new Array(len)
    console.log(dp);
    dp[0] = data[0];
    //
    for (let i = 1; i < len; i++) {
        debugger
        // dp[i] 表示在第 i 天结束时的最小花费
        // i    i-1      data[i]       dp[i - 1]
        // 1     0   +     1   1     初始值 1
        // 2     1   +     2   2           3  (data[1] + dp[i - 1]) 2 + 1(上次值)
        // 3     2   +     3   3           6  (data[2] + dp[i - 1]) 3 + 3(上次值)
        // 4     3   +     3   4           9  (data[3] + dp[i - 1]) 3 + 6(上次值)
        // 5     4   +     2   5          11  (data[3] + dp[i - 1]) 2 + 9(上次值)
       console.log(dp)
        // 表示当前天的花费加上前一天的累计花费
        dp[i] = dp[i - 1] + data[i];

        // console.log('i', i,dp[i - 1] ,i-1, data[i],dp[i - 1] + data[i],dp,);
        // j 初始化为 i - 1,表示从当前天的前一天开始。
        // 循环从当前天的前一天开始,到当前天与 k 的差值(即 j >= i - k + 1)为止。
        // 循环结束后,dp[i] 表示在第 i 天结束时的最小花费。

        for (let j = i - 1; j >= i - k + 1 && j >= 0; j--) {
            dp[i] = Math.min(dp[i], dp[i - 1] + data[j]);
        }
    }
    // console.log(dp);
    return dp[len - 1];
}

function main() {
    // Add your test cases here
    console.log('minCost:', solution(5, 2, [1, 2, 3, 3, 2])); // 输出最小花费
    // console.log('minCost:', solution(6, 3, [4, 1, 5, 2, 1, 3])); // 输出最小花费
    // console.log('minCost:', solution(4, 1, [3, 2, 4, 1])); // 输出最小花费
}
main()

通过动态规划的方法,计算出一个新的数组 new_dp,其中每个元素 new_dp[j] 表示在某种条件下的最优解。

动态规划的核心思想是通过子问题的解来构建原问题的解,从而避免重复计算,提高效率。

function solution(n, k, data) {
    // 创建一个大小为 k + 10 的整数数组 dp。
    let dp = new Array(k + 1).fill(Infinity);
    // 初始化 dp 数组的前 k 个元素 并且大于0
    for (let i = 1; i <= k; i++) {
        // i = 1  data[0]  1   dp[i] = 1* 1
        // i = 2  data[0]  1   dp[i] = 2* 1
        dp[i] = i * data[0];
    }
    console.log(dp);
    dp[k + 1] = Infinity;
    console.log(dp);
    // 外层循环遍历 data 数组的每个元素(从第 1 个到第 n-1 个)。
    for (let i = 1; i < n; i++) {
        // 创建一个新数组 new_dp,用于存储计算结果。
        let new_dp = new Array(k + 1).fill(Infinity);
        console.log('new_dp',new_dp);
        new_dp[k + 1] = Infinity;
        // 内层循环更新 new_dp 数组,计算每个状态的最小值
        for (let j = 1; j <= k; j++) {
            // 初始化 new_dp[j] 为无穷大  当前值
            new_dp[j] = Infinity;
            // 对于每个 j,遍历从 1 到 j + 1 的所有值 w
            for (let w = 1; w <= j + 1; w++) {
                // 找到 new_dp[j] 的最小值
                // 其中 dp[w] 是前一个状态的值,
                // (j - w + 1) * data[i] 是当前状态的增量
                new_dp[j] = Math.min(new_dp[j], dp[w] + (j - w + 1) * data[i]);
            }
        }
        dp = new_dp;
    }

    return dp[1];
}

function main() {
    // Add your test cases here
    console.log('minCost:', solution(5, 2, [1, 2, 3, 3, 2])); // 输出最小花费
    console.log('minCost:', solution(6, 3, [4, 1, 5, 2, 1, 3])); // 输出最小花费
    console.log('minCost:', solution(4, 1, [3, 2, 4, 1])); // 输出最小花费
}

main();