一、问题描述
小R正在计划一次从地点A到地点B的徒步旅行,总路程需要 N 天。为了在旅途中保持充足的能量,小R每天必须消耗1份食物。幸运的是,小R在路途中每天都会经过一个补给站,可以购买食物进行补充。然而,每个补给站的食物每份的价格可能不同,并且小R最多只能同时携带 K 份食物。
现在,小R希望在保证每天都有食物的前提下,以最小的花费完成这次徒步旅行。你能帮助小R计算出最低的花费是多少吗?
二、测试样例
样例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
三、思路分析
题目的标签就是动态规划,这题我们当然要用动态规划来解答,我们需要找到一种最佳状态,使得在每一天都能以最小的花费购买足够的食物,同时不超过携带上限 K。
3.1 状态定义
dp[i][j]表示在第i天结束时,携带j份食物的最小花费。我们最终要找的就是dp[n-1][1], 也就是第i-1天结束的时候,正好携带了1份食物(这份食物第i天吃)的值。
3.2 初始化
int[][] dp = new int[n][k+1]
以样例1为例,dp = int[5][3], 其初始化状态如下图所示
其中第一天携带的多少食物对应多少花费的状态我们很容易就能算出来,这里我们添加了边界条件方便后续状态转移,因为本示例中k=2,因此设置k=3的状态为max,表示是一个不可达的状态,具体到java代码里,可以是一个INTETER.MAXVALUE。
3.3 状态转移
下面我们先手动找一下规律
dp[1][0]
也就是第一天的时候,我们有0份食物,这个状态可以通过哪个状态得来呢?很明显,只能是通过
- 第0天的时候,有1份食物的状态转换而来,也就是通过
dp[0][1]
dp[1][1]
- 也就是第一天的时候,我们有1份食物,这个状态可以通过哪个状态得来呢?依此类推,是通过第0天的时候,有1份食物,且第1天又购买了一份的状态转换而来,也就是通过
dp[0][1]+data[1] - 第0天有2份食物,且第1天没有购买任何食物得来的,也就是
dp[0][2]
dp[1][2] 也就是第一天的时候,我们有2份食物,这个状态可以通过哪个状态得来呢?依此类推,是通过
- 第0天的时候,有1份食物,且第1天又购买了2份的状态转换而来,也就是通过
dp[0][2]+ 2*data[1] - 第0天有2份食物(当然这个状态是不可能的,通过设置max就是为了防止这种情况的发生),且第1天又购买了1份食物得来的,也就是
dp[0][2] + data[1] - 第0天有3份食物(当然这个状态是不可能的,通过设置max就是为了防止这种情况的发生),且第1天没有购买任何食物,也就是
dp[0][3]
从上述推理我们发现,dp[0][0]其实并没有参与到推理当中,由此可知,我们并不需要推理dp[i][0] 0<i<n 如下图所示部分
从对于dp[1][2]的推理也能发现,推导dp[i][j]的时候,需要涉及从 dp[i-1][1]到dp[i-1][j+1]这几个连续的状态,如下图所示
由此可得以下转移方程
- 转移方程为:
dp[i][j] = Math.min(dp[i][j], dp[i - 1][w] + (j - w + 1) * data[i]),其中w的范围是从1到j + 1。
四、代码实现
public class Main {
public static int solution(int n, int k, int[] data) {
int[][] dp = new int[n + 10][k + 10];
// 初始化第0天的状态
for (int i = 1; i <= k; i++) dp[0][i] = i * data[0];
// 初始化超出携带上限的状态
for (int i = 0; i < n; i++) dp[i][k + 1] = 0x3f3f3f3f;
// 动态规划状态转移
for (int i = 1; i < n; i++) {
for (int j = 1; j <= k; j++) {
dp[i][j] = 0x3f3f3f3f;
for (int w = 1; w <= j + 1; w++) {
dp[i][j] = Math.min(dp[i][j], dp[i - 1][w] + (j - w + 1) * data[i]);
}
}
}
// 返回最终结果
return dp[n - 1][1];
}
public static void main(String[] args) {
// 测试样例
System.out.println(solution(5, 2, new int[]{1, 2, 3, 3, 2}) == 9);
}
}