01背包问题 最低级理解

01背包问题

下面给出 5 个物品信息和 1 个体积为 10 的背包:
number 1 2 3 4 5
volume 2 2 4 5 6
price 2 3 2 4 5
提出问题:找到这个背包能装下物品的最大价值是多少?
对于蒟蒻来说,这个问题很好解,直接模拟过程,遍历每个物品,选或不选,即0和1中做出选择,之后找到所有情况中的价值最大的那种搭配就行。
如果你是这样想的,那么我可以告诉你,你一定可以求出答案!
但是如果我把问题扩大一点:有1e6个物品信息,背包体积为capacity , 求最大价值。
那么你还能用同样的方法解出来吗?
所以这时候我们就要找到一个更加高效的算法。
下面分析问题: 从1e6个物品中找最大价值,我们最多要经过2的1e6次方的运算(天文数字),那么造成这么大的运算的直接原因是重复运算次数太多。(实际上是因为数据过大
举个例子,如果我要从1e6个物品中找出一个物品,使包内物体的价值最大,那么想都不要想,找 price 最高的。那么如果从1e6个物品中找 二 个物品,使包内价值最大怎么找呢?
蒟蒻的算法是这个样子的:如果第一个选 物品a,如果第二个物品选 b ,那么他们的总价值的就是V1, 记录V1的值,然后进行下一轮:如果第一个物品选的是a,第二个物品选的是c,那么他们的价值是V2,记录下来,然后重复上述步骤。这样下来,我们的运算次数最大是1e12
那么我们现在优化一下:我们已经找到了包中装一个物品的最大价值,运算次数最大为1e6,那么我们现在只用找1e6个物品中第二大的物品,运算次数最大为1e6,那么这两个物品的价值和一定是最大的,且运算次数为2*1e6。和蒟蒻算法比价一下,不知道快了多少!


看完例子,相信大家已经知道什么是动态规划了吧!划重点:通过保存中间结果来避免不必要的重复计算,从而保证效率。
那么我们的优化代码怎么写呢?如下:

#include<bits/stdc++.h>
using namespace std;
int volume[100],price[100];
int v[100][100]={0};
int main()
{
	int T,capacity;
	cin>>T>>capacity;
	for(int i=1;i<=T;i++)
	{
		cin>>volume[i]>>price[i];
	}
	for(int i=1;i<=T;i++)
	{
		for(int j=1;j<=capacity;j++)
		{
			if(v[i-1][j-volume[i]]+price[i]>v[i-1][j]&&j>=volume[i])
			{
				v[i][j]=v[i-1][j-volume[i]]+price[i];
			}
			else 
			{
				v[i][j]=v[i-1][j];
			}
		}
	 } 
	 cout<<v[T][capacity]<<endl;
	return 0;
 } 

没看懂?待我细细道来:
我们先说v[n][m]的意义:其作用就是记忆过程,表示当剩余容积为m,从v个物体中放任意的物体时,包内的最大价值
首先,我们必须明确:
1,在 0 个物体里选任意个物体放入背包,无论背包剩余容积为多少,其最大价值为 0 。(因为没东西了)
2,当背包的容积为0时,从 n 个物体中选任意个物体放入背包,包内最大价值为0(因为放不进去了)
所以v[0][n]和v[n][0](n表示任意值)都为0。


之后解释这段代码(核心代码):

if(v[i-1][j-volume[i]]+price[i]>v[i-1][j]&&j>=volume[i])

什么意思呢?这样想吧:现在我们走到了第 i 个物品前,我们要考虑一下到底要不要把这个物品放进包内。
打个比方:同样重量的砖头和金子,你会选哪个?(选砖头的自己拍死自己去吧! )为了使价值最大,肯定选的是金子。那么同样的,我选择放不放这个物品的依据就是:
我放了这个物品后的总重量是W,总价值是V1。放其他任意物品的总重量也是W,价值是V2。
那么如果V1>V2,就说明你很聪明,选了一个价值更高的东西。
所以就有这个判断:

v[i-1][j-volume[i]]+price[i]>v[i-1][j]

如果V1>V2,就跟新数据V[i][j],表示在这个状态下,包内的最大价值为多少。
如果V1<=V2,就将前面的数据代替到当前数据下。


所以通过两重循环,这个问题就解决了!

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值