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

46 阅读3分钟
这道题描述的是小R可同时携带K份食物要从地点A到地点B,需要N天时间,每天都要消耗掉一份食物,且每天都能进行食物补给,在每天都有食物吃的情况下,如何以最小的花费完成这次徒步旅行,N表示的是总路程所需的时间,K表示的是小R每天最多可同时携带食物的数量。这道题目考察的是动态规划,贪心算法,背包问题,数组操作,条件判断和循环,时间复杂度和空间复杂度。
解题思路:首先进行动态规划来解决问题,定义一个二维数组dp[i][j],用来表示第i天携带了j份食物的最小会花费,接着用状态转移方程,当前天不购买食物,如果第i-1天携带了j+1份食物,则第i天结束携带了j份食物,花费为dp[i-1][j+1],当前天购买食物,如果第i-1天携带了j份食物,则第i天结束携带了j+buy份食物,花费为dp[i-1][j]+buy&data[i-1],而buy是购买的食物份数,接着初始条件是第0天结束时,没有食物,花费为0,最后确定最终结果是第N天结束时携带0份食物的最小花费是dp[N][0]。
这道题我们值得注意的是最小花费的更新,使用Math.min在更新dp[i][j]时,更新的都是最小花费,避免直接赋值,确保状态转移的正确性;时间复杂度为O(N*K^2),确保在合理的时间内解决问题,空间复杂度为O(N*K),确保不会占用过多的内存。
边界检查确保j>0时才进行dp[i][j-1]的更新,避免数组越界,购买食物的循环确保buy的值在合理的范围内,buy<=K且j+buy<=K考察的是动态规划;最小堆用来存储食物的价格,每次从堆中取出最小值进行购买,考察的是贪心算法;确保每天携带的食物不超过k份考察的是背包问题;外层循环遍用来历每一天,内层循环用来遍历每一种可能的食物携带数量考察的是双重循环;初始化用来将dp数组的所有元素初始化Integer.MAX_VALUE,表示不可达状态考察的是数组操作。
以下是该题代码:
public class Main {
    public static int solution(int n, int k, int[] data) {
       
        int[][] dp = new int[n + 1][k + 1];
        
        for (int i = 0; i <= n; i++) {
            for (int j = 0; j <= k; j++) {
                dp[i][j] = Integer.MAX_VALUE;
            }
        }
    
        dp[0][0] = 0;
        for (int i = 1; i <= n; i++) { 
            for (int j = 0; j <= k; j++) {  
                if (dp[i - 1][j] != Integer.MAX_VALUE) {
                    if (j > 0) {
                        dp[i][j - 1] = Math.min(dp[i][j - 1], dp[i - 1][j]);
                    }
                    for (int buy = 1; buy <= k && j + buy <= k; buy++) {
                        dp[i][j + buy - 1] = Math.min(dp[i][j + buy - 1], dp[i - 1][j] + buy * data[i - 1]);
                    }
                }
            }
        }
        return dp[n][0];
    }
    
    public static void main(String[] args) {
        System.out.println(solution(5, 2, new int[]{1, 2, 3, 3, 2}) == 9);  // 输出 9
    }
}