最少花费购买食物问题解析与解题思路| 豆包MarsCode AI刷题

91 阅读4分钟

这是一道经典的贪心算法题目,要求规划从起点到终点的购买策略,以使总花费最小。下面整理题目理解、解题思路和实现步骤,方便复习和参考。


问题分析

小U需要在从地点A到地点B的徒步旅行中保证每天有食物,同时需要从不同价格的补给站中购买以实现 总花费最小。关键问题在于如何规划每一天的购买数量和购买地点。

输入输出分析

  • 输入:

    • M:总路程的天数。

    • N:补给站的数量。

    • p:补给站信息,p[i] = [A, B] 表示第 A 天有补给站,每份食物的价格为 B

      • 保证第 0 天一定有补给站。
      • 补给站按照天数从小到大排列。
  • 输出:

    • 购买食物的最小总花费。

示例

  • 输入:

    plaintext
    复制代码
    m = 5, n = 4, p = [[0, 2], [1, 3], [2, 1], [3, 2]]
    
    • 共需 5 天。
    • 有 4 个补给站分别在第 0 天、第 1 天、第 2 天、第 3 天,价格依次为 2, 3, 1, 2。
  • 输出:

    plaintext
    复制代码
    7
    
    • 最优策略:

      1. 在第 0 天购买 2 份(消耗当天和第 1 天)。

      2. 在第 2 天购买 3 份(供第 2, 3, 4 天)。

      • 总花费:2×2 + 1×3 = 7

解题思路

问题建模

  • 每一天消耗一份食物。

  • 每个补给站的价格不同,我们需要决定在哪些天购买多少食物,以满足全程需求,并使总花费最小。

  • 贪心策略适合解决这类问题,即:

    1. 优先在价格便宜的补给站购买更多食物。
    2. 在高价补给站尽量少买,甚至不买。

核心思想

  • 模拟每天的行程,同时维护当前持有的食物数量。
  • 当即将缺少食物时,根据价格和剩余天数选择最优的购买策略。
  • 使用 优先队列(小根堆) 来动态管理价格最低的补给站。

详细实现步骤

1. 初始化

  • 使用一个变量 current_food 表示当前持有的食物数量。
  • 用一个优先队列(最小堆)记录当前所有可用的补给站(根据价格排序)。
  • 总花费初始化为 0

2. 按天模拟

  • 遍历每一天,从第 0 天到第 M-1 天。

    1. 每天先消耗一份食物,current_food -= 1
    2. 如果当前天有补给站,将其加入优先队列(按价格排序)。
    3. 当食物不足时,从优先队列中购买最便宜的食物,直到满足需求。

3. 贪心购买

  • 当天食物不足时:

    • 从价格最低的补给站(堆顶)购买食物。
    • 更新总花费和当前食物数量。

4. 特殊情况

  • 如果在任何时刻,优先队列为空且食物不足,则问题无解。

代码实现

import java.util.PriorityQueue;

public class Main {
    public static int solution(int m, int n, int[][] p) {
        // Edit your code here
        int[] dp = new int[m + 1];
        for (int i = 1; i <= m; i++) {
            dp[i] = Integer.MAX_VALUE; // Set a high initial cost for each day
        }
        dp[0] = 0; // Starting point has no cost

        // Priority queue to store available stations, sorted by price (min-heap)
        PriorityQueue<int[]> minHeap = new PriorityQueue<>((a, b) -> a[1] - b[1]);

        int stationIndex = 0;
        for (int day = 0; day <= m; day++) {
            // Add new stations that are available on this day
            while (stationIndex < n && p[stationIndex][0] == day) {
                minHeap.offer(p[stationIndex]);
                stationIndex++;
            }

            // If we need food today and there's an available station
            if (dp[day] != Integer.MAX_VALUE && !minHeap.isEmpty()) {
                // Buy food at the lowest price available from the heap
                int[] cheapestStation = minHeap.peek();
                int price = cheapestStation[1];

                // Only update dp[day + 1] if day + 1 is within bounds
                if (day + 1 <= m) {
                    dp[day + 1] = Math.min(dp[day + 1], dp[day] + price);
                }
            }
        }

        return dp[m];
    }

    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}}) == 7);
    }
}

复杂度分析

  1. 时间复杂度

    • 遍历所有天数和补给站:O(M + N)
    • 堆操作(插入/弹出):O(log N)
    • 总复杂度:O((M + N) log N)
  2. 空间复杂度

    • 存储补给站的堆:O(N)

总结与扩展

总结

  1. 贪心思想:优先从价格最低的补给站购买足够的食物。
  2. 动态维护最优解:利用优先队列快速获取当前最便宜的补给站。
  3. 边界处理:确保在缺少食物时优先购买,避免因缺少食物导致旅行失败。

扩展

  1. 更多限制条件:如果每个补给站能购买的食物数量有限,需要在贪心时额外处理购买数量。
  2. 其他算法:对于补给站数量庞大的场景,可用动态规划方法优化搜索路径。