题目思路分析:38补给站最优花费问题 | 豆包MarsCode AI 刷题

153 阅读4分钟

考察算法:动态规划

问题描述

小U计划进行一场从地点A到地点B的徒步旅行,旅行总共需要 M 天。为了在旅途中确保安全,小U每天都需要消耗一份食物。在路程中,小U会经过一些补给站,这些补给站分布在不同的天数上,且每个补给站的食物价格各不相同。

小U需要在这些补给站中购买食物,以确保每天都有足够的食物。现在她想知道,如何规划在不同补给站的购买策略,以使她能够花费最少的钱顺利完成这次旅行。

  • M:总路程所需的天数。
  • N:路上补给站的数量。
  • p:每个补给站的描述,包含两个数字 A 和 B,表示第 A 天有一个补给站,并且该站每份食物的价格为 B 元。

保证第0天一定有一个补给站,并且补给站是按顺序出现的。

算法思路分析

首先我们对于要如何购买其实有两种选择,其一就是购买之后去下一站购买,其二就是在本站购买一定数量的食物,然后在n站之后(没有食物时)在该站购买新的食物。我们不难发现,如果要使得花费最少,我们需要在花费尽量少的站购买尽可能多(直到遇到比起本站食物价格还要低)的食物,然后在该站再次购买食物。

这个问题是关于小U徒步旅行中的食物补给站花费优化。整体思路是通过动态规划的方式,逐步计算到达每一天的最小花费,考虑每个补给站的情况,更新后续天数的最小花费,最终得到完成整个旅行(第M天)的最小花费。下面我会通过对于代码的逐步分析给大家阐述具体应该如何实现。

    1. 初始化:我们先创建一个dp数组用于存储到达每一天的最小花费,(ps:很多初学动态规划的同学比较容易把dp数组都初始化为0,这点希望大家能够根据题意来进行合理的初始化),由于我们需要求解最小的花费,因此在初始化dp数组的时候我们把它赋值为INT_MAX,这样可以方便我们后续操作。
    std::vector<int> dp(m + 1, INT_MAX);
    dp[0] = 0;
    1. 动态规划主体算法:
  • 首先我们需要通过循环遍历数组中的每个元素
  • 获取该补给站的天数day = p[i][0]和价格cost = p[i][1]
  • 内层循环从该补给站所在的天数day开始,一直到旅行的总天数m。如果dp[day]不等于INT_MAX,表示该天是可达的(已经计算出到达该天的花费),那么就更新dp[j]的值。更新的逻辑是dp[j] = std::min(dp[j], dp[day]+(j - day)*cost);,这里是比较当前的dp[j](之前计算得到的到达第j天的最小花费)和从day天到达第j天(需要购买j - day份食物,每份食物价格为cost)的花费加上到达day天的花费(dp[day])中的较小值。
    for (int i = 0; i < n; ++i) {
        int day = p[i][0];
        int cost = p[i][1];   
        // 更新从该天开始到最后一天的最小花费
        for (int j = day; j <= m; ++j) {
            if (dp[day]!= INT_MAX) {
                // 确保该天可达
                dp[j] = std::min(dp[j], dp[day] + (j - day) * cost);
            }
        }
    }

完整代码如下(仅供参考):

#include <iostream>
#include <vector>
#include<bits/stdc++.h>
using namespace std;

int solution(int m, int n, std::vector<std::vector<int>> p) {

    std::vector<int> dp(m + 1, INT_MAX);
    dp[0] = 0;

    // 遍历每个补给站
    for (int i = 0; i < n; ++i) {
        int day = p[i][0];
        int cost = p[i][1];   
        // 更新从该天开始到最后一天的最小花费
        for (int j = day; j <= m; ++j) {
            if (dp[day]!= INT_MAX) {
                // 确保该天可达
                dp[j] = std::min(dp[j], dp[day] + (j - day) * cost);
            }
        }
    }

    return dp[m];
}

int main() {
    // Add your test cases here
    std::cout << (solution(5, 4, {{0, 2}, {1, 3}, {2, 1}, {3, 2}}) == 7);
    return 0;
}

总结:通过这种动态规划的方法,通过逐步构建子问题的最优解,最终得到全局最优解。通过动态规划的方法避免了暴力解法的复杂性,能够有效的节省是空间复杂度。通过本道问题的解析,相信大家对于动态规划已经有了一定的理解,在这里给大家推荐一道动态规划比较经典的题目,用最小花费爬楼梯,主题思路与本题类似,但是思路稍微有一点点改变。

感谢大家的观看,若有不足请不吝赐教。