“无敌复杂”“无敌难”的C++背包问题

背包问题总是被许多“年轻”同学叨叨:“诶亚,这么难,我做不了! ”“我才不想做哩
今天,我给大家分享整理一下我做背包问题的整体思路与算法。

背包问题的终极框架:“01背包”

【例n】有一个旅行者,他有一个无敌的背包,容量大得很:V。然后,他有N件不知名的物品,把某件物品放入背包可以获得这件物品的价值c,又会使无敌背包减少还能装下的重量。所以,现在他想要让背包装得下,又要让自己获得最大的价值,请问,应该放哪些物品呢?

第一眼看上去似乎难到不得了。我第一次学习背包问题时的想法使者样的:(请勿学习)
“嘿嘿,我把每个物品的价值除以重量,不就得到了平均价值了吗?”
似乎看上去很对呀~ 可惜,我没办法把费用和价值放在一块,却又增加一个平均价值?岂不是更复杂?

希望读者都学过动态规划。。在这里我就讲一下不讲了吧 ~~谅解谅解

终极框架的特点

最基础最基础···(*100)的背包问题,每个物品只有一个,放,或者不放。
啊哈,“01”的意思就是一个。真是明白。

推导子问题定义状态

用f[i][v] 表示前i件物品(不放、部分、全部)放入容量为v的背包可以获得的最大价值,则其状态转移方程就是:
f ( i ) ( v ) = m a x ( f ( i − 1 ) ( v ) , f ( i − 1 ) ( v − w ( i ) ) + c ( i ) ) f(i)(v)=max(f(i-1)(v),f(i-1)(v-w(i))+c(i)) f(i)(v)=max(f(i1)(v),f(i1)(vw(i))+c(i))
如果你没看懂,那恭喜你,你终于迈不进迈进了背包问题的大门。
别着急,我来讲解一下:
f ( i ) ( v ) f(i)(v) f(i)(v)
不用讲了吧
max应该也知道了吧(虽然我第一次时不知道max是最大的意思)
f ( i − 1 ) ( v ) f(i-1)(v) f(i1)(v)
意思是第i件物品不放进去,重量没有变化,但处在的物品发生了改变,所以i-1
f ( i − 1 ) ( v − w ( i ) ) + c ( i ) f(i-1)(v-w(i))+c(i) f(i1)(vw(i))+c(i)
第i件物品放进去,首先是容量发生变化,减掉本次物品重量。其次加上物品的价值。
如果你这个方程看懂了,以后的其他什么“完全背包”“多重背包”的方程,你就能看的懂。

注意! 本方程有一个缺点,f[i][v]有意义且仅当存在一个前i个物品的子集,其费用为v。所以,最终的答案并不是f[i][v]而是f[i][0…V]的最大值。不过,你只要把每一个f[i][j](读入)的初始值复位0,你就会发现f[i][v]也是最终答案。

优化时间与空间

这样一来,时间复杂度和空间复杂度均为 O ( N ∗ V ) O(N*V) O(NV)。时间复杂度基本不能在进行优化了,但空间复杂度却可以优化为 O ( V ) O(V) O(V)

在这里,我们先考虑上面的思路怎样实现。肯定有一个主循环for(1…n)每次算出来二维数组f[i][0~V]的所有值。如果只用一个数组f[0…V]能不能保证第i次循环完后f[v]中表示着f[i][v]呢?实际,这要求在每次主循环中我们以v=V…0的逆序推f[v],这样能保证f[v]时f[v-w[i]]保存的是状态f[i-1][v-w[i]]的值。所以,伪代码如下:

for(i=1...N)
	for(v=V...0)
		f[v]=max{f[v],f[v-w[i]]+c[i]};

其中,for(v=V…0)0是不必要的。都没有空间了,你还烦什么?所以,修改:

for(i=1...N)
	for(v=V...w[i])
		f[v]=max{f[v],f[v-w[i]]+c[i]};

最后,提供上最终的代码:

#include<iostream>
#include<cstdio>
using namespace std;
int thing[1000001][3];         //重量(w)是thing[?][0],价值(c)是thing[?][1],f是thing[?][3] 
int max(int a,int b)
{
	 return a>b? a:b;
}
int main()
{
	 int n,V;
	 cin>>V>>n;
	 for(int i=1;i<=n;i++) cin>>thing[i][0]>>thing[i][1];
	 for(int i=1;i<=n;i++)
		for(int v=V;v>=thing[i][0];v--)
		{
			thing[v][2]=max(thing[v][2],thing[v-thing[i][0]][2]+thing[i][1]);
 		}
	cout<<thing[V][2];
	return 0;
}

之后我会继续发布出其他有关于背包的文章。
本人第一次写文章,有问题请各位大大们提出,谢谢。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值