要解决小明徒步问题,目标是在确保每天消耗1份食物的情况下,以最小的花费完成M天的徒步。为了实现这一目标,我们需要制定一个策略,选择在合适的补给站购买足够的食物,以覆盖接下来的几天,并尽可能利用价格较低的补给站。
解题思路
- 添加虚拟补给站: 为了简化问题,我们在终点B(第M天)添加一个虚拟的补给站,价格为0元。这样可以确保我们最终能覆盖到终点。
- 寻找下一个价格更低的补给站: 对于每一个补给站,找到其后面第一个价格更低的补给站。这可以通过使用单调栈(Monotonic Stack)高效地实现。
- 计算最小花费: 从起点开始,依次在每个补给站购买足够的食物,以达到下一个价格更低的补给站。这样可以确保在价格较高的补给站购买的食物尽可能少,从而最小化总花费。
详细步骤
-
初始化:
- 读取M(总天数)和N(补给站数量)。
- 读取N个补给站的信息,并将其存储在一个数组中。
- 在数组末尾添加一个虚拟补给站,位置为M天,价格为0元。
-
使用单调栈找到每个补给站的下一个价格更低的补给站:
- 从后往前遍历补给站。
- 使用一个栈来保持补给站的索引,栈内的补给站价格递增。
- 对于当前补给站,弹出所有价格不低于当前补给站的补给站,直到找到第一个价格更低的补给站。
- 记录这个补给站的位置作为当前补给站的下一个价格更低的补给站。
-
遍历补给站,计算最小花费:
- 从起点开始,依次在每个补给站购买足够的食物,以覆盖到下一个价格更低的补给站。
- 累加每次购买的花费,直到覆盖整个M天的徒步。
代码实现
以下是基于上述思路的Java实现:
import java.util.ArrayDeque;
public class Main {
public static int solution(int m, int n, int[][] p) {
// 创建一个新的数组,包含N+1个补给站,最后一个是虚拟补给站
int[][] stations = new int[n + 1][2];
for(int i = 0; i < n; i++) {
stations[i][0] = p[i][0]; // A天
stations[i][1] = p[i][1]; // B价格
}
stations[n][0] = m; // 虚拟补给站的位置
stations[n][1] = 0; // 虚拟补给站的价格
// 计算每个补给站的下一个价格更低的补给站
int[] nextLower = new int[n + 1];
ArrayDeque<Integer> stack = new ArrayDeque<>();
for(int i = n; i >= 0; i--){
while(!stack.isEmpty() && stations[stack.peekLast()][1] >= stations[i][1]){
stack.removeLast();
}
if(stack.isEmpty()){
nextLower[i] = n; // 如果没有更低价格的补给站,指向虚拟补给站
}
else{
nextLower[i] = stack.peekLast();
}
stack.addLast(i);
}
// 遍历补给站,计算总花费
long total = 0;
int current = 0;
while(current < n){
int j = nextLower[current];
int distance = stations[j][0] - stations[current][0];
total += (long) distance * stations[current][1];
current = j;
}
return (int) total;
}
public static void main(String[] args) {
// 测试用例
System.out.println(solution(5, 4, new int[][]{
{0, 2},
{1, 3},
{2, 1},
{3, 2}
}) == 7); // 输出:7
// 其他测试用例
System.out.println(solution(2, 1, new int[][]{
{0, 1}
}) == 2); // 输出:2
System.out.println(solution(3, 2, new int[][]{
{0, 1},
{1, 0}
}) == 1); // 输出:1
System.out.println(solution(5, 1, new int[][]{
{0, 0}
}) == 0); // 输出:0
}
}
代码解析
-
补给站数组的构建:
stations数组包含所有N个补给站的信息,以及一个虚拟补给站。- 虚拟补给站的位置为第M天,价格为0元。
-
单调栈的使用:
- 我们从后往前遍历所有补给站。
- 对于每个补给站,弹出栈中所有价格不低于当前补给站价格的补给站。
- 栈顶元素即为当前补给站的下一个价格更低的补给站。
- 如果栈为空,说明当前补给站之后没有价格更低的补给站,指向虚拟补给站。
-
总花费的计算:
- 从起点开始,依次在每个当前补给站购买足够的食物,以覆盖到下一个价格更低的补给站。
- 累加每次购买的花费,直到覆盖所有M天。
-
测试用例:
- 提供了多个测试用例,包括样例输入和其他边界情况,确保算法的正确性。
复杂度分析
- 时间复杂度:O(N),其中N是补给站的数量。单调栈的每个元素最多进出栈一次。
- 空间复杂度:O(N),用于存储补给站信息和单调栈。
总结
通过使用单调栈,我们能够高效地找到每个补给站的下一个价格更低的补给站,并据此制定购买策略,以最小化总花费。这种方法不仅简洁,而且在处理大规模数据时也能保持高效。