补给站最优花费问题 | 豆包MarsCode AI 刷题

108 阅读5分钟

在 “补给站最优花费问题” 中,我们面临的任务是帮助小U在从地点A到地点B的徒步旅行中,合理规划在各个补给站购买食物的策略来实现总花费最少的目标。这一问题不仅考验我们对逻辑的推理,也需要我们巧妙地运用算法思维去找到最优解。

首先我们来探讨解题思路。由于补给站是按顺序出现且第0天一定有补给站,我们可以从前往后逐步分析每个补给站的情况:
①当只有一个补给站时,那么小U只能在这一个补给站购买所有天数所需的食物,花费即为该补给站食物价格乘以总天数。
②当有多个补给站时,对于每个补给站,我们比较它与前一个补给站食物价格的高低,如果当前补给站食物价格更低,那么从当前补给站购买到下一个补给站出现前一天所需的食物量是更优的选择;如果当前补给站食物价格更高,那么我们延续使用前一个补给站的价格来购买这一段路程所需食物,这样可以避免高价购买带来的额外花费。而对于最后一个补给站,我们需要特殊处理,如果它的价格比前一个补给站低,那么就用它的价格购买剩余天数的食物,否则就使用前一个补给站的价格。
通过这样的方式,我们能够在遍历补给站的过程中,逐步确定每个阶段的最优购买策略,从而累计得到最少的总花费。

下面是详细的解析代码:

public class Main {
    public static int solution(int m, int n, int[][] p) {
        // 如果只有一个补给站,直接返回该补给站食物价格乘以总天数
        if (n == 1) {
            return p[0][1] * m;
        }
        int result = 0;
        // 先处理从第 0 天到第 1 天的情况,购买第 1 天所需食物量乘以第 0 天补给站的价格
        result += p[1][0] * p[0][1];
        for (int i = 1; i < n; i++) {
            // 对于最后一个补给站进行特殊处理
            if (i == n - 1) {
                // 如果最后一个补给站价格更低,就用它的价格购买剩余天数的食物
                if (p[i][1] < p[i - 1][1]) {
                    result += (m - p[i][0]) * p[i][1];
                } else {
                    // 否则用前一个补给站价格购买剩余天数食物
                    result += (m - p[i][0]) * p[i - 1][1];
                }
                break;
            }
            // 如果当前补给站价格比前一个补给站低
            if (p[i][1] < p[i - 1][1]) {
                // 计算从当前补给站到下一个补给站出现前一天所需食物量乘以当前补给站价格,并累加到总花费
                result += (p[i + 1][0] - p[i][0]) * p[i][1];
            } else {
                // 如果当前补给站价格更高,将当前补给站价格设为前一个补给站价格
                p[i][1] = p[i - 1][1];
                // 计算从当前补给站到下一个补给站出现前一天所需食物量乘以前一个补给站价格,并累加到总花费
                result += (p[i + 1][0] - p[i][0]) * p[i - 1][1];
            }
        }
        return result;
    }

    public static void main(String[] args) {
        System.out.println(solution(5, 4, new int[][]{{0, 2}, {1, 3}, {2, 1}, {3, 2}}) == 7);
        System.out.println(solution(6, 5, new int[][]{{0, 1}, {1, 5}, {2, 2}, {3, 4}, {5, 1}}) == 6);
        System.out.println(solution(4, 3, new int[][]{{0, 3}, {2, 2}, {3, 1}}) == 9);
    }
}

solution方法中,我们首先判断补给站数量是否为1,如果是,则直接计算并返回总花费,如果不是,则接下来初始化总花费result,并先计算从第0天到第1天的花费。接着通过循环遍历补给站,对于非最后一个补给站,比较当前补给站与前一个补给站价格。如果当前补给站价格低,就按照当前价格购买到下一个补给站前所需食物量并累加花费;如果价格高,就将当前补给站价格替换为前一个补给站价格,再按照该价格计算花费。对于最后一个补给站,再次比较价格,根据情况计算剩余天数的花费并累加到result

例如,对于输入m = 5, n = 4, p = [[0, 2], [1, 3], [2, 1], [3, 2]]。首先,我们判断补给站数量不为1,所以我们初始化result为第1天所需食物量(1)乘以第0天补给站价格(2),得到2。然后进入循环,当i = 1时,因为第2个补给站价格(1)比第1个补给站价格(3)低,所以从第2个补给站购买从第2天到第3天所需食物量(2)乘以其价格(1),result变为 4。当i = 2时,因为第3个补给站价格(2)比第2个补给站价格(1)高,所以按照第2个补给站价格计算从第3天到第4天所需食物量(2)乘以价格(1),result变为 6。当i = 3时,处理最后一个补给站,因为其价格(2)比前一个补给站价格(1)高,所以按照前一个补给站价格购买剩余1天的食物,result变为 7,最终返回 7。

最后我们来看一下时间复杂度和空间复杂度。在时间复杂度方面,代码主要是一个循环遍历补给站的过程,循环次数为补给站数量n,所以时间复杂度为O(n)。在空间复杂度方面,除了存储输入数据的二维数组p和几个临时变量外,没有额外大量的数据结构使用,所以空间复杂度较低,很大程度取决于输入数据的大小。