徒步旅行中的补给问题 | 豆包MarsCode AI 刷题

260 阅读3分钟

徒步旅行补给问题算法分析笔记|豆包MarsCode AI 刷题

一、问题描述

小 R 计划从地点 A 到地点 B 进行徒步旅行,总行程需 N 天。旅行中每天需消耗 1 份食物,且每天会经过一个补给站,补给站食物价格各异,小 R 最多同时携带 K 份食物。要求在保证每天有食物的前提下,计算完成旅行的最小花费。

输入示例 n = 5, k = 2, data = [1, 2, 3, 3, 2],其中 n 表示旅行天数,k 表示小 R 最多携带的食物份数,data 数组表示每天补给站食物的价格。

解决思路-动态规划思想

使用一个List模拟小 R 的背包。遍历每一天行程,把当天补给站食物加入背包,若背包满(>k),则减少前面加入的食物,食用背包中最小价格的食物。

通过模拟实际的补给和使用食物的过程来计算花费。主要是需要每次遍历都需要找出背包最小值,以及加入删除操作当 k 较大时,可能会影响整体性能。

动态规划步骤:

  • 定义动态规划数组 dp[],其中 dp[i] 表示从第 0 天到第 i 天完成徒步旅行的最小花费。
  • 使用容器 pq存储当前携带的食物价格,模拟小 R 携带的食物,其大小最多为 k
  • 初始化 dp[0] = data[0],并将 data[0] 加入优先队列。
  • 对于第 i 天(i > 0):先将 dp[i - 1]+minPrice 的值赋给 dp[i];若优先队列大小超过 k,则移除pq首元素。

这种思路利用了动态规划的思想,通过记录子问题的最优解来逐步构建全局最优解。遍历为O(n),获取最小值O(k),算法的操作时间复杂度为O(n*k),逻辑较为清晰,代码实现相对简洁。

四、代码实现

以下是思路的 Java 代码实现:

    public static int solution(int n, int k, int[] data) {
        // Edit your code here
        int[] dp = new int[n];
        List<Integer> pq = new ArrayList<>();
        dp[0] = data[0];
        pq.add(data[0]);
        for (int i = 1; i < n; i++) {
            pq.add(data[i]);
            if (pq.size() > k) {
                pq.remove(0);
            }
            dp[i] = dp[i - 1] + Collections.min(pq);

        }
        return dp[n - 1];
    }

五、代码讲解

  • 动态规划数组dp初始化:创建一个长度为n的整数数组dp,用于存储从第 0 天到第i天完成徒步旅行的最小花费。首先将dp[0]初始化为data[0],因为第一天只能在当天的补给站购买食物,此时的最小花费就是第一天补给站食物的价格。
  • 模拟优先队列pq初始化:创建一个ArrayList来模拟优先队列。将第一天补给站食物的价格data[0]添加到pq中。
  • 添加当天食物价格到pq:在每次循环中(对应每一天的行程),将当天补给站食物的价格data[i]添加到pq中。
  • 处理超出携带容量的情况:如果pq的大小(即小 R 携带的食物份数)超过了k,就移除pq中的第一个元素(这里假设pq中第一个元素是最早添加的,通过pq.remove(0)实现)。这一步是为了模拟小 R 最多只能携带k份食物。
  • 计算当前天的最小花费dp[i] :计算第i天的最小花费dp[i]。这里的计算方式是将前一天的最小花费dp[i - 1]加上当前pq中价格最低的食物价格(通过Collections.min(pq)获取)。这种计算方式的逻辑是,小 R 可以选择使用前一天剩下的食物(花费dp[i - 1]),然后再加上今天购买一份最便宜的食物(从pq中获取最小值)的花费。
  • 最后,返回dp[n - 1],即整个徒步旅行n天的最小花费。这个值代表了小 R 完成从地点 A 到地点 B 的徒步旅行,在给定的补给站价格和携带食物份数限制下的最优花费策略的结果。

可以考虑使用优先队列优化