蓝桥算法两周训练营--Day2:DP

T1:P1048 [NOIP2005 普及组] 采药 - 洛谷

代码:

1、二维Dp:

package 蓝桥算法两周训练营__普及组.Day2_dp;

import java.util.Scanner;

/**
 * @author yx
 * @date 2023-02-05 13:16
 */
public class t1 {
    //    P1048 [NOIP2005 普及组] 采药 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        int T=scanner.nextInt();
        int M=scanner.nextInt();
        int[] values=new int[M+1];
        int[] times=new int[M+1];
        for (int i = 1; i <= M; i++) {
            times[i]=scanner.nextInt();
            values[i]=scanner.nextInt();
        }
        int[][] dp=new int[T+1][M+1];
        for (int i = 1; i <= T ; i++) {
            for (int j = 1; j <= M ; j++) {
                if(i<times[j]){
                    //不采第j个草药
                    dp[i][j]=dp[i][j-1];
                }else {
                    //可以不采第j个草药,也可以采,选择最大价值的方案
                    dp[i][j]=Math.max(dp[i][j-1],dp[i-times[j]][j-1]+values[j]);
                }
            }
        }
        System.out.println(dp[T][M]);
    }
}

分析1:

时间T其实对应于背包问题中的总体积T,M对应于物品个数

思路:

1、先依次遍历从背包容量为i(1<=i<=T)开始

2、在不超过背包容量i的前提基础上(i>=times[j]),尽可能地往背包里塞东西

3、如何判断是否要装入物品j呢?我们可以判断不装物品j(dp[i][j-1])与装入物品j(dp[i-times[j][j-1]]+values[j])进行比较,取最优的解

2、一维Dp:

package 蓝桥算法两周训练营__普及组.Day2_dp;

import java.util.Scanner;

/**
 * @author yx
 * @date 2023-02-06 16:39
 */
public class t1_01背包_一维 {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        int T=scanner.nextInt();
        int M=scanner.nextInt();
        //背包总容量T
        int[] dp=new int[T+1];
        int[] times=new int[M+1];
        int[] values=new int[M+1];
        //背包的体积和价值
        for (int i = 1; i <= M; i++) {
            times[i]=scanner.nextInt();
            values[i]=scanner.nextInt();
        }
        //先遍历每一件物品i(1<=i<=M)
        for (int i = 1; i <=M ; i++) {
            //对背包容量进行遍历,从最大的容量T开始,一直往下到times[i]
            // 即再--的话,j<times[i]就不能装下物品i了
            for (int j = T; j >= times[i] ; j--) {//逆序遍历
                dp[j]=Math.max(dp[j],dp[j-times[i]]+values[i]);
            }
        }
        System.out.println(dp[T]);
    }
}

分析2:

让我假设现在的背包的容量是 C=10;

物品编号:1   2   31   2   3

物品重量:5   6   45   6   4

物品价值:20 10 1220 10 12


直接分析dp数组:

dp:0 0 0 0 0 0 0 0 0 0

i=1:
dp[10] = max(dp[5]+20, dp[10]);
dp[9] = max(dp[4]+20, dp[9]);
dp[8] = max(dp[3]+20, dp[8]);
dp[7] = max(dp[2]+20, dp[7]);
dp[6] = max(dp[1]+20, dp[6]);
dp[5] = max(dp[0]+20, dp[5]);

dp: 0 0 0 0 20 20 20 20 20 20

i=2:
dp[10] = max(dp[6]+4, dp[10]);
dp[9] = max(dp[3]+10, dp[9]);
dp[8] = max(dp[2]+10, dp[8]);
dp[7] = max(dp[1]+10, dp[7]);
dp[6] = max(dp[0]+10, dp[6]);

dp: 0 0 0 0 20 20 20 20 20 20 //看到了没,选10的都被之前的20压下去了

i=3:
dp[10] = max(dp[6]+12, dp[10]);
dp[9] = max(dp[5]+12, dp[9]);
dp[8] = max(dp[4]+12, dp[8]);
dp[7] = max(dp[3]+12, dp[7]);
dp[6] = max(dp[2]+12, dp[6]);
dp[5] = max(dp[1]+12, dp[5]);
dp[4] = max(dp[0]+12, dp[4]);

dp: 0 0 0 12 20 20 20 20 32 32

dp[10] 就是背包容量为 10 的时候的最大价值,就是要求的值了,可以看到,容量大的时候的值取决于容量小的时候的值,从而不断被正确更新,所以用一维 dp 的时候,j 的循环必须是从大到小逆序开始的,逆序,就防止了一个物品放入多次!!!否则...........

T2:P1616 疯狂的采药 - 洛谷

代码:

package 蓝桥算法两周训练营__普及组.Day2_dp;

import java.util.Scanner;

/**
 * @author yx
 * @date 2023-02-05 13:16
 */
public class t2_完全背包 {
//    ​P1616 疯狂的采药 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)​
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        int T=scanner.nextInt();
        int M=scanner.nextInt();
        int[] times=new int[M+1];
        int[] values=new int[M+1];
        //十年IO一场空,不开long long 见祖宗
        long[] dp=new long[T+1];
        for (int i = 1; i <= M; i++) {
            times[i]=scanner.nextInt();
            values[i]=scanner.nextInt();
        }
        for (int i = 1; i <= M; i++) {
            //从times[i]开始可以一直加同一种物品,直到时间的最大限制(即体积的最大容量)
            for (int j = times[i]; j <= T ; j++) {
                //先把背包装满,再依次迭代最优解
                dp[j]=Math.max(dp[j],dp[j-times[i]]+values[i]);
            }
        }
        System.out.println(dp[T]);
    }
}

分析:

递推式:

思想:先把背包尽可能装满,再依次迭代最优解

首先dp数组初始化全为0:给定物品种类有4种,包最大体积为5,数据来源于题目的输入
v[1] = 1, w[1] = 2
v[2] = 2, w[2] = 4
v[3] = 3, w[3] = 4
v[4] = 4, w[4] = 5

i = 1 时: j从v[1]到5
dp[1] = max(dp[1],dp[0]+w[1]) = w[1] = 2 (用了一件物品1)
dp[2] = max(dp[2],dp[1]+w[1]) = w[1] + w[1] = 4(用了两件物品1)
dp[3] = max(dp[3],dp[2]+w[1]) = w[1] + w[1] + w[1] = 6(用了三件物品1)
dp[4] = max(dp[4],dp[3]+w[1]) = w[1] + w[1] + w[1] + w[1] = 8(用了四件物品1)
dp[5] = max(dp[3],dp[2]+w[1]) = w[1] + w[1] + w[1] + w[1] + w[1] = 10(用了五件物品)

i = 2 时:j从v[2]到5
dp[2] = max(dp[2],dp[0]+w[2]) = w[1] + w[1] = w[2] =  4(用了两件物品1或者一件物品2)
dp[3] = max(dp[3],dp[1]+w[2]) = 3 * w[1] = w[1] + w[2] =  6(用了三件物品1,或者一件物品1和一件物品2)
dp[4] = max(dp[4],dp[2]+w[2]) = 4 * w[1] = dp[2] + w[2] =  8(用了四件物品1或者,两件物品1和一件物品2或两件物品2)
dp[5] = max(dp[5],dp[3]+w[2]) = 5 * w[1] = dp[3] + w[2] =  10(用了五件物品1或者,三件物品1和一件物品2或一件物品1和两件物品2)

i = 3时:j从v[3]到5
dp[3] = max(dp[3],dp[0]+w[3]) = dp[3] = 6 # 保持第二轮的状态 
dp[4] = max(dp[4],dp[1]+w[3]) = dp[4] = 8 # 保持第二轮的状态 
dp[5] = max(dp[5],dp[2]+w[3]) = dp[4] = 10 # 保持第二轮的状态

i = 4时:j从v[4]到5
dp[4] = max(dp[4],dp[0]+w[4]) = dp[4] = 10 # 保持第三轮的状态
dp[5] = max(dp[5],dp[1]+w[4]) = dp[5] = 10 # 保持第三轮的状态

总结:

一维01背包:

从最大容量T开始到第i件物品容量times[i],逆序遍历,没有重复,是01背包

一维完全背包:

从第i件物品容量times[i]开始到最大容量T,正序遍历,有重复,是完全背包

课后习题

T3:1015. 摘花生 - AcWing题库

T4:P1077 [NOIP2012 普及组] 摆花 - 洛谷

  • 9
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 8
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 8
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小羊不会飞

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值