1. 问题描述
小R正在计划一次从地点A到地点B的徒步旅行,总路程需要 N 天。为了在旅途中保持充足的能量,小R每天必须消耗1份食物。幸运的是,小R在路途中每天都会经过一个补给站,可以先购买完食物后再消耗今天的1份食物。然而,每个补给站的食物每份的价格可能不同,并且小R在购买完食物后最多只能同时携带 K 份食物。
现在,小R希望在保证每天食物消耗的前提下,以最小的花费完成这次徒步旅行。你能帮助小R计算出最低的花费是多少吗?
输入
n总路程需要的天数k小R最多能同时携带食物的份数data[i]第i天补给站每份食物的价格
输出
- 返回完成这次徒步旅行的最小花费
约束条件
1<n,k<10001<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];
}