关于01背包问题

关于01背包问题

一、题目

链接:https://www.nowcoder.com/questionTerminal/708f0442863a46279cce582c4f508658来源:牛客网

现有一个容量大小为V的背包和N件物品,每件物品有两个属性,体积和价值,请问这个背包最多能装价值为多少的物品?

###输入描述:

第一行两个整数V和n。接下来n行,每行两个整数体积和价值。1≤N≤1000,1≤V≤20000。每件物品的体积和价值范围在[1,500]。

###输出描述:

输出背包最多能装的物品价值。

示例1

###输入

6 3
3 4
2 5
4 2

###输出

9

二、题目解析

将物品在规定的容量下装总价值最大的物品

三、做法解析

思考一下:在什么情况下我们能得到最大的价值?

因为容量有限,所以我们无法全部装完

那么,如果我们在容量相同的情况下,来选择我们总价值最高的物品呢?

这样是不是一个最优的?

这应该比较容易想到确实是这样。

所以我们现在来解决怎么在容量相同的情况下选择总价值最高的物品

首先我们不考虑代码,我们先手算一遍

容量为6的时候,我们应该取哪些?

根据样例的数据,很明显我们应该选择容量3和2的物品来达到最大总价值9

那我们是怎么选择的呢?

计算机可不像我们可以同时处理多条数据并自动进行分析

但是根据我们上面的红字提示来一步一步分析

1、假如我们要放第一个物品,那么是不是容量为3,4,5,6至多到6的背包都能放下呢?因为初始条件下,我们的背包价值为空,这时候随便一个有价值的对于这个背包来说i都是最高的

2、然后我们需要放第二个物品,那这时候就要考虑他能放在容量为多少的背包里面了,假如背包都是空的,那么2,3,4,5,6至多到6容量的背包都能将其放进去,容量为2的背包我们没有装,所以容量为2的背包直接装进去即可,但是容量为3的背包已经被前面一个物品装满了,那我们是不是就直接跳过呢?那肯定不行对吧,如果容量为2的物品价值比容量为3的物品价值高呢?那我们是不是就需要更新?所以我们需要进行判断,判断一下容量为2的物品价值是否比现在背包中装的价值要高,这就完了吗?既然我们是放在容量为3的背包中,那肯定有同学就要问了,那还留了一个空位就不管了吗?不,这个空位我们当然要填满,但是容量为1的背包又没有装物品,但是假如装了物品呢?所以即使为0,我们也需要考虑进去,这时候我们就需要一开始初始化我们所有容量的背包了,然后我们用当前的价值加上容量减去放当前物品剩下的容量的价值,比如这个容量为3的包放下容量为2的物品后我们还要加上容量为1的物品(即使容量为1的物品没有),这样我们才算填满了。之后我们继续走,容量为4的包是不是应该和容量为3的包一样的判断方法,我们将第二个物品放进去之后加上剩余容量为2的之前的包中的东西取过来?(第二个物品确实放在了容量为2的包,但是我们不能放两个容量为2的物品),所以这时候我们就发现了一个很严重的问题,在我们前面的运算中会把同样的东西放两遍,这是什么原因造成的?是不是因为我们是从小的容量开始放造成的?如果我们一开始放的是容量为最大(样例规定最大容量为6)的包,就不会重复拿到了吧,因为我们容量小的不会去拿容量大的物品,所以这时候问题就解决了,我们放物品的时候,先从大的包里面塞,这样塞小包的时候就不会导致一个物品装两次的情况了(图1充分表明),后面的情况都一致。

这里我们就能得到公式了dp[j] = max(dp[j - w[i]]+v[i], dp[j]);

没错,我就是图一:
在这里插入图片描述

啊哈哈我过了(大声.jpg)

AC代码:(注释版)

#include<iostream>

using namespace std;

int v, n, weight[1010], value[1010], dp[20010];	//全局变量数组自动初始化为0,这样就不用我手动初始化了(真妙!) 

int main()
{
	
	int v, n;
	cin >> v >> n; //读入背包最大容量和物品数目 
	
	for(int i = 1; i <= n; i ++)	//读入每个物品的重量及对应价值 
		cin >> weight[i] >> value[i];
		
	for(int i = 1; i <= n; i ++){
		for(int j = v; j >= weight[i]; j --){	//从最大的包开始装,一直装到当前物品的重量即可,前面的都装不下就没必要继续循环了(而且还能防止j-weight[i]的数组越界) 
						//从取和不取中选一个大的来保存,具体看讲解 
			dp[j] = max(dp[j - weight[i]]+value[i], dp[j]);
		}
	}
	//输出最大容量的那个包就是最大总价值	
	cout << dp[v];
	
	return 0;
}

PS:

​ 如果还有不理解的欢迎留言询问,如果多数人不理解,可以考虑出一期视频讲解!(祝各位题目全绿!)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值