徒步旅行中的补给问题

350 阅读2分钟

1. 问题描述

小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

2. 解题方法一 (贪心法)

function solution(n, k, data) {
  let res = 0;
  // 这是我的背包
  let bag = [];
  // 要开始买了
  for (let i = 0; i < n; i++) {
    // 如果背包是空的,直接买满
    if (bag.length === 0) {
      while (bag.length !== k) {
        let food = { index: i }
        res += data[i];
        bag.push(food);
      }
    } else {
      // 临时背包,整理一下食品
      let temp = [];
      for (let j = 0; j < bag.length; j++) {
        if (data[bag[j].index] > data[i]) {
          // 看看背包里面食品的价格,比当前贵的直接退款
          res -= data[bag[j].index];
        } else {
          // 便宜的直接放进包里
          temp.push(bag[j]);
        }
      }
      // 把背包装满
      while (temp.length !== k) {
        let food = { index: i }
        res += data[i];
        temp.push(food);
      }
      bag = temp;
    }
    // 走了一天了,干饭,先吃便宜的,贵的可以等退款
    let min_val_food = bag[0];
    for (let j = 1; j < bag.length; j++) {
      if (data[min_val_food.index] > data[bag[j].index]) {
        min_val_food = bag[j];
      }
    }
    bag.splice(bag.indexOf(min_val_food), 1);
  }
  // 走到终点了,把多余的食物全部卖了
  while (bag.length > 0) {
    let remove = bag.shift();
    res -= data[remove.index];
  }
  return res;
}

解题思路

先直接买满背包,遇到便宜的就将贵的退掉换成便宜的,先吃包里面最便宜的,到终点就退掉多余的食物。

缺点是:大量的嵌套循环,效率比较低

3. 解题方法二 (动态规划)

function solution(n, k, data) {
  // 创建dp数组,这里注意,最后一天已经到了,就不用买食物了
  let dp = new Array(n).fill(0);
  // 基本事件:第一天至少要买一份食物
  dp[0] = data[0];
  // 迭代:dp[i]代表当负重为k时,第i天路程的最小花费(食物刚好吃完)
  for (let i = 1; i < n; i++) {
    // 直接买
    dp[i] = dp[i - 1] + data[i];
    // 之前买的,吃库存
    for (let j = i - 1; j >= i - k + 1 && j >= 0; j--) {
      dp[i] = Math.min(dp[i], dp[i - 1] + data[j]);
    }
  }
  return dp[n - 1];
}