青训营X豆包MarsCode 技术训练营动态规划| 豆包MarsCode AI 刷题

72 阅读5分钟

动态规划问题

旅行中的徒步补给问题
1.题目思路

这段代码的核心思想是动态规划,通过比较直接购买食物和使用之前购买的食物两种策略,来找到每一天的最小花费。代码通过两层循环实现了状态转移,外层循环遍历每一天,内层循环考虑在当前天数下,如何利用之前购买的食物来最小化花费。最终,代码返回的是整个旅程的最小花费。

2.详细方法

创建dp数组: dp数组用于存储到达每一天结束时的最小花费。数组的长度与data相同,因为data数组包含了每一天的食物价格。

初始化: dp[0] = data[0]:第一天至少要买一份食物,所以第一天的最小花费就是第一份食物的价格。

动态规划: 外层循环i从1到length-1lengthdata数组的长度),代表从第二天开始到最后一天。内层循环ji-1递减到i-k+1,这个循环的目的是考虑在第i天,小R可以购买从第j天到第i天的食物,以确保在第i天结束时食物刚好吃完。dp[i] = dp[i-1] + data[i]:如果小R在第i天直接购买食物,那么第i天的花费就是前一天的花费加上第i天的食物价格。dp[i] = Math.min(dp[i], dp[i-1] + data[j]):如果小R在第i天使用之前购买的食物,那么第i天的花费就是前一天的花费加上第j天的食物价格。这里取dp[i]dp[i-1] + data[j]的最小值,以确保找到最小的花费。

返回结果: return dp[length-1]:返回最后一天的最小花费,即整个旅程的最小花费。

3.代码实现
public class Main {
    public static int solution(int n, int k, int[] data) {
        // Edit your code here

        int length = data.length;
        //创建dp数组,这里注意,最后一天已经到了,就不用买食物了
        int[] dp = new int[length];
        //基本事件:第一天至少要买一份食物
        dp[0] = data[0];
        //迭代:dp[i]代表当负重为k时,第i天路程的最小花费(食物刚好吃完)
        for(int i = 1;i < length;i++){
            //直接买
            dp[i] = dp[i-1] + data[i];
            //之前买的,吃库存
            for(int j = i - 1;j >= i-k+1 && j >= 0;j--){
                dp[i] = Math.min(dp[i],dp[i-1] + data[j]);
            }
        }
        return dp[length-1];
    }
    
    public static void main(String[] args) {
        // Add your test cases here

        System.out.println(solution(5, 2, new int[]{1, 2, 3, 3, 2}) == 9);
    }
}
补给站最优花费问题
1.题目思路

这段代码的核心思想是动态规划,通过比较直接购买食物和使用之前购买的食物两种策略,来找到每一天的最小花费。代码通过两层循环实现了状态转移,外层循环遍历每一天,内层循环考虑在当前天数下,如何利用之前购买的食物来最小化花费。最终,代码返回的是整个旅程的最小花费。

2.详细方法

创建dp数组: dp数组用于存储到达每一天结束时的最小花费。数组的长度与data相同,因为data数组包含了每一天的食物价格。

初始化: dp[0] = data[0]:第一天至少要买一份食物,所以第一天的最小花费就是第一份食物的价格。

动态规划: 外层循环i从1到length-1lengthdata数组的长度),代表从第二天开始到最后一天。内层循环ji-1递减到i-k+1,这个循环的目的是考虑在第i天,小R可以购买从第j天到第i天的食物,以确保在第i天结束时食物刚好吃完。dp[i] = dp[i-1] + data[i]:如果小R在第i天直接购买食物,那么第i天的花费就是前一天的花费加上第i天的食物价格。dp[i] = Math.min(dp[i], dp[i-1] + data[j]):如果小R在第i天使用之前购买的食物,那么第i天的花费就是前一天的花费加上第j天的食物价格。这里取dp[i]dp[i-1] + data[j]的最小值,以确保找到最小的花费。

返回结果return dp[length-1]:返回最后一天的最小花费,即整个旅程的最小花费。

3.代码实现
public class Main {
   public static int solution(int m, int n, int[][] p) {
       // Edit your code here
       //dp数组的含义:第i天结束时剩余j份补给所花费的价钱
       int[][] dp = new int[m+1][m+1];
       //标记下一个补给站的索引
       int index = 0;
       //初始化dp数组
       for(int i = 0;i <= m;i++){
           dp[0][i] = p[index][1] * i;
       }
       index++;
       //第i天
       for(int i = 1;i <= m;i++){
           //剩余j份食物
           for(int j = 0;j < m;j++){
              //剩余的食物足够走完了
               if(j >= i -m){
                   dp[i][j] = dp[i - 1][j+1];
               }
               //不补给的情况
               dp[i][j] = dp[i-1][j+1];
               //判断今天是否到达补给站
               if(index < p.length && p[index][0] == i){
                   //有补给站,购买k份食物
                   for(int k = 0;k <= j;k++){
                       dp[i][j] = Math.min(dp[i][j],dp[i-1][j - k + 1] + k * p[index][1]);
                   }
               }
           }
           //如果今天有补给站,补给站指针移动
           if(index < p.length && p[index][0] == i){
               index++;
           }
       }
       return dp[m][0];
   }

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