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

369 阅读4分钟

原题截图

image.png

题意分析

原题题意分析

小R一共有N天的旅行旅程,每天需要消耗1份食物,每天都会遇到一个补给站,每个补给站每份食物的价格不同,最多只能同时携带K份食物。

题意猜测

由于原题题面描述不够清楚,部分题意需要自己猜测。

  1. 在旅行开始前,小R能否携带食物?
  2. 每天可以消耗完食物,再去购买食物(将食物补充到K个),还是需要购买完所有食物后再消耗食物?也就是说,在每天购买完食物并消耗完当天的食物后,小R最多可以拥有K份还是K-1份食物?

编写代码运行,并和题目的样例数据对照,可以猜到如下题意:

  1. 在旅行开始前,小R不能携带任何食物。
  2. 小R每天必须购买完所有食物后再消耗食物,即在每天购买完食物并消耗完当天的食物后,小R最多可以拥有K-1份食物

解题思路

本题解题的关键是得出小R在哪几天分别购买多少食物,即小R购买食物的策略,解决策略问题的两种常用的算法就是贪心和动态规划。经过思考,可以发现简单的贪心算法无法得出最优解,于是考虑使用动态规划

使用动态规划解决本题时,可以定义二维数组int dp[][]dp[i][j]代表小R在前i天结束时(购买完食物且消耗完当天食物后),携带j份食物所需要花费的最小金额。转移状态时,可以使用三层循环,并定义状态转移方程dp[i][j] = min(dp[i][j], dp[i-1][j-(x-1)] + x*a[i-1]);,即i天结束时携带j份食物的最小金额,等于在第i-1天拥有j-(x-1)份食物的情况下,第i天购买x份食物后,前i天花费的总金额。对于初始状态,由于小R在旅行开始前不能携带任何食物,所以初始状态为dp[0][0] = 0

细节处理方面,由于状态转移时需要从dp[i-1][j-(x-1)]处继承,而dp[][]数组的第二维只有[0,k1][0,k-1]区间的下标是有意义的,因此继承时x的值需要保证0 <= j-(x-1) <= k-1,以x为未知量解不等式,可得j-k+2 <= x <= j+1,用这个不等式可以确定第三层循环中x的取值范围

最后,当整个dp[][]数组规划完成后,容易得出,由于题意没有对最后一天结束后食物的数量做出特殊要求,并且整个旅途中的任何一份食物都需要花费金钱,所以最后一天结束时,小R不剩余任何食物的方案就是最优的,所以dp[n][0]为答案n为旅行天数)。

代码实现

#include <bits/stdc++.h>
using namespace std;
int solution(int n, int k,const vector<int>& a)
{
    constexpr int inf=0x3f'ff'ff'ff;
    //dp[i][j]:在第i天买完食物并消耗完1份食物后,携带j份食物所花费的最小金额
    vector<vector<int>> dp(n+1,vector<int>(k,inf));
    dp[0][0] = 0;
    for(int i=1;i<=n;i++)
    {
        for(int j=0;j<k;j++)
        {
            // 如果可以每天先消耗食物再购买食物
            // (或者先购买食物,然后消耗食物,最后再购买食物),则:
            // 0 <= j-(x-1) <= k
            // 0 <= j-x+1 <= k
            // -j-1 <= -x <= -j+k-1
            // j-k+1 <= x <= j+1
            // 经检验,这个题意不正确。
            // --------------------
            // 如果必须在每天必须在购买完食物后再消耗食物
            // (即消耗食物后无法再购买食物),
            // 则每天结束时实际最多可携带的食物为k-1个,所以:
            // 0 <= j-(x-1) <= k-1
            // 0 <= j-x+1 <= k-1
            // -j-1 <= -x <= -j+k-2
            // j-k+2 <= x <= j+1
            // 经检验此题意正确。
            for(int x=max(0,j-k+2);x <= j+1;x++)
            {
                dp[i][j] = min(dp[i][j],dp[i-1][j-(x-1)]+x*a[i-1]);
            }
        }
    }
    return dp[n][0];
}

总结

本题使用动态规划算法解决,时间复杂度为O(NK2)O(NK^2),空间复杂度为O(NK)O(NK)。如果你有复杂度更优的解法,欢迎在下方评论。