蓝桥杯每日一题------背包问题(一)

本文详细介绍了背包问题中的01背包和完全背包,并通过动态规划的步骤讲解了如何定义和推导状态转移方程,提供了相应的代码示例,包括如何优化dp数组以降低空间复杂度。

点击可观看配套视频讲解

背包问题

阅读小提示:这篇文章稍微有点长,希望可以对背包问题进行系统详细的讲解,在看的过程中如果有任何疑问请在评论区里指出。因为篇幅过长也可以进行选择性阅读,读取自己想要的那一部分即可。

前言

背包问题可以看作动态规划系列入门的一个开端,欢迎开启动态规划之旅,在正式学习之前,我想说的是,动态规划真的不难,与贪心算法比较,动态规划有自己的多种板子,也有自己的多种套路;与高级数据结构比较,动态规划的代码量真的非常友好;与字符串类算法比较,动态规划没有那么抽象,ok话不多说,开始吧。
首先介绍一下动态规划的步骤(我自己总结的,自己用起来感觉还不错,y总也有介绍过闫式dp分析法,大家感兴趣可以看一看,怎么方便怎么来)
求解动态规划有两个大的阶段,分别是定义dp数组和推导状态转移方程。大家觉得这两个哪个重要呢?诚然状态转移方程是动态规划的关键,但是我在做题的过程中感受到当你的dp数组定义正确了,状态转移方程的推导就是自然而然的事情,所以对我来说,最关键的是定义dp数组。我们可以按照下面的步骤定义dp数组。
第一步:缩小规模。大家在大学学到动态规划时,一般都会拿来和贪心比,和分治比,无论哪一个我们都不能一口吃个胖子,都是从最基础的那个地方开始,一步一步往下走,最终走到终点。既然要缩小规模,那必然要有一个维度来定义当前的规模,放在背包问题里,规模就是考虑的物品的个数,那么用一个维度就可以了,放在区间dp里,规模是区间的大小,而不同的区间结果是不一样的,所以需要两个维度来表示区间的左右端点。
第二步:限制。放在背包问题里,限制就是背包的容量,你选的物品的总体积不能超过当前背包容量,所以你需要一个维度来表示当前的体积。
第三步:写出dp数组。走到这里,根据规模和限制定义了dp数组,dp[i][j]表示当前考虑了前i个物品,背包容量为j时能够装的最大价值。我们求的就是最大价值,那么dp数组对应的值就是最大价值,一般和所求是一样的,求什么就记录什么。
第四步:修改dp数组。这一步就是在写状态转移方程时,你发现定义的dp数组维度少了,还需要其它信息,那么这个时候就是需要什么往dp数组里面加什么,即增加维度,但是要注意一点,一般dp数组的维度和时间复杂度是正相关的,维度过多,很有可能超时。

01背包

在这里插入图片描述
定义dp数组
第一步:缩小规模。考虑n个物品,那我就先考虑1个物品,在考虑2个物品…,需要一个维度表示当前考虑的物品个数。
第二步:限制。所选物品个数不能超过背包容量,那么需要一个维度记录当前背包的容量。
第三步:写出dp数组。dp[i][j]表示当前考虑了前i个物品,背包容量为j时的最大价值。
第四步:推状态转移方程。dp[i][j]应该从哪里转移过来呢,必然是从前i-1个物品转移,我要考虑两种情况,对于第i个物品,可以选择要它,也可以不要它,如果要第i个物品,我就需要背包里面给我预留出第i个物品的体积,也就是从a[i-1][j-v[i]]转移,同时也能获得该物品的价值。如果不要第i个物品,那么之前从前一个状态相同容量的背包转移过来就行,即a[i-1][j]。
综上状态转移方程如下
a[i][j] = max(a[i-1][j],a[i-1][j-v[i]]+w[i])
考虑写代码了
第一步:确定好遍历顺序,对于背包问题,一般第一个for遍历规模,第二个for遍历限制。

for(int i = 1;i <= n;i++) {
   
   
		for(int j = 1;j <= m;j++) {
   
   
			dp[i][j] = dp[i-1][j];//为什么要在这里转移,因为这个转移是一定会发生的,而另一个转移不一定会发生
			if(j>=v[i])
				dp[i][j] = Math.max(dp[i-1][j-v[i]]+w[i], dp[i][j]);
		}
	}

第二步:考虑是否要对dp数组初始化,这里不需要,因为最开始的状态考虑前0个物体,它的值就是0,不需要管。
全部代码如下,

import java.util.Scanner;
public class Main {
   
   
public static void main(String[] args) {
   
   
	Scanner scanner = new Scanner(System.in);
	int n = scanner.nextInt();
	int V = scanner.nextInt();
	int[] v = new int[n+1];
	int[] w = new int[n+1];
	for (int i = 1; i < w.length; i++) {
   
   
		v[i] = scanner.nextInt();
		w[i] = scanner.nextInt();
	}
	int[][] dp = new int[n+1][V+1];
//	for (int i = 0; i < dp.length; i++) {
   
   
//		dp[0][i] = 1;
//	}
	for (int i = 1; i < dp.length; i++) {
   
   
		for (int j = 0; j < V+1; j++) {
   
   
			dp[i][j] = Math.max(dp[i][j], dp[i-1][j]);
			if(v[i]<=j) {
   
   
				dp[i][j] = Math.max(dp[i][j], dp[i-1][j-v[i]]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值