问题描述
小U计划进行一场从地点A到地点B的徒步旅行,旅行总共需要 M 天。为了在旅途中确保安全,小U每天都需要消耗一份食物。在路程中,小U会经过一些补给站,这些补给站分布在不同的天数上,且每个补给站的食物价格各不相同。
小U需要在这些补给站中购买食物,以确保每天都有足够的食物。现在她想知道,如何规划在不同补给站的购买策略,以使她能够花费最少的钱顺利完成这次旅行。
M:总路程所需的天数。N:路上补给站的数量。p:每个补给站的描述,包含两个数字A和B,表示第A天有一个补给站,并且该站每份食物的价格为B元。
保证第0天一定有一个补给站,并且补给站是按顺序出现的。
算法选择
dfs + 记忆化搜索
算法思路
-
初始化:
- 创建一个二维数组
memo,用于存储动态规划的结果,其中memo[cur][food]表示在第cur天,剩余food份食物时的最小花费。 - 将
memo数组中的所有元素初始化为-1,表示尚未计算。 - 创建一个
HashMap,将每个补给站的天数和食物价格存储起来。
- 创建一个二维数组
-
深度优先搜索(DFS) :
- 定义一个递归函数
dfs(map, M, cur, food),表示在第cur天,剩余food份食物时的最小花费。 - 如果
food < 0,表示食物不足,返回一个很大的值(Integer.MAX_VALUE / 2),表示这种情况不可能发生。 - 如果
cur == M,表示旅行结束,返回0,表示不需要再购买食物。 - 如果
memo[cur][food]不等于-1,直接返回memo[cur][food]的值,表示已经计算过。 - 如果第
cur天有补给站,遍历所有可能购买的食物数量i(从0到M - cur - food),计算购买i份食物后的最小花费,并更新res。 - 如果第
cur天没有补给站,直接计算不购买食物的最小花费。
- 定义一个递归函数
-
返回结果:
- 最终,
dfs(map, M, 0, 0)的值就是从第0天开始,剩余0份食物时的最小花费。
- 最终,
代码展示
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
public class Main {
private static int[][] memo;
public static int solution(int m, int n, int[][] p) {
// Edit your code here
// 对于今天, 我可以买补给, 不买不补给
memo = new int[m + 1][m + 1];
for (int i = 0; i < m + 1; i++) {
Arrays.fill(memo[i], -1);
}
Map<Integer, Integer> map = new HashMap<>();
for (int i = 0; i < n; i++) {
map.put(p[i][0], p[i][1]);
}
int res = dfs(map, m, 0, 0);
return res;
}
public static int dfs(Map<Integer, Integer> map, int M, int cur, int food) {
// 非法
if (food < 0) {
return Integer.MAX_VALUE / 2;
}
// 正常
if (cur == M) {
return 0;
}
if (memo[cur][food] != -1) {
return memo[cur][food];
}
int res = Integer.MAX_VALUE;
if (map.containsKey(cur)) {
for (int i = 0; i <= (M - cur - food); i++) {
res = Math.min(res, dfs(map, M, cur + 1, food + i - 1) + i * map.get(cur));
}
return memo[cur][food] = res;
} else {
return memo[cur][food] = dfs(map, M, cur + 1, food - 1);
}
}
public static void main(String[] args) {
// Add your test cases here
System.out.println(solution(5, 4, new int[][] { { 0, 2 }, { 1, 3 }, { 2, 1 }, { 3, 2 } }));
}
}