【C++背包】生活中不可能出现的背包问题

上次,我们讲了最基础最基础…的01背包问题,这次,我们就来讲一下关于生活中不可能出现的背包问题

无敌完全背包问题

经典问题

【例n+1】之前的旅行者回来了。他胜利归来,带来了很多很多东西。又过了几天,他的手的脚又开始痒痒了。于是,他又开始准备东西,继续去探险。他有N种不同物品,又有一个容量为V的背包,每件物品都可以无限拿,。让这位旅行者获得最大的生存几率,然后又使背包不会超限,请变出编出程序,帮助他。

提前透露下哈,这人姓鲁。至于名,自己猜吧。

基本思路

这个问题,非常不接近接近01背包问题,所不同的是每件物品有无限种!!!(天哪)从每件物品的角度去思考,不仅仅是拿和不拿,而是拿一件,两件,······(无限件) 。如果按照解01背包问题的思路,f[i][v]表示的是前i种物品放入一个容量为v的背包的最大利益。所以,按照这个思路,可以根据每种物品不同的策略写出状态转移方程,like this:
f ( i ) ( v ) = m a x ( f ( i 1 ) ( v k w ( i ) ) + k c ( i ) 0 < k w ( i ) < = v ) f(i)(v)=max(f(i-1)(v-k*w(i))+k*c(i)|0<k*w(i)<=v)
这样,根据01基础背包问题的思路就求出了这样不清晰 清晰的方法。

优化

这个算法使用一维数组的话,根据01背包问题改进版,伪代码如下:

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

诶亚,这和01背包问题只是差一个V次循环的方向而已。为什么这一改就可以了呢?首先回顾下上次01背包问题为什么要进行逆序搜寻。

在这里,我们先考虑上面的思路怎样实现。肯定有一个主循环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]};

重点来了,“实际,这要求在每次主循环中我们以v=V…0的逆序推f[v],这样能保证f[v]时f[v-w[i]]保存的是状态f[i-1][v-w[i]]的值”。于是,我们就能看出,这是因为要保证第i次循环中的状态f[i][v]是由状态f[i-1][v-w[i]]推导而来。换句话说,这正是为了保证每件物品只能选择1次,保证在考虑“选入第i件物品”这个策略时,依据的是一个不是已经放入了第i件物品的子结果f[i-1][v-w[i]]。
看得懂吗? 我似乎也看不懂。。。
让我们简单来说明吧。状态表:设有3件物品,价值分别为1,2,3,重量分别为2,3,1,背包重量为5。这时,从01背包思想考虑,双逆推产生的状态表:

i 3 2 1
v 5,3 5,3,2,0 5,3,2,2,1,0
f 0,1 0,1,2,5 0,1,2,4,5,3

f[v]=max{f[v],f[v-w[i]]+c[i]} 在双逆推中,f[v]不可能是由f[v]本身推出。
逆推+顺推

i 1 2 3
v 1,3,5 //此处1,3,5都推出,向右看: 0,2 0 //此处的0是由两个1号+1个3号得来 ,0,1,1,2,2,3,4,5
f 1,0

f[v]=max{f[v],f[v-w[i]]+c[i]}在单顺单逆中,f[v]可能是由f[v]本身推出。
这下看懂了吧。

继续优化

根据上次for优化经验,优化v=V…w[i],同样,没得放了,你还放个啥?
伪代码如下:

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

经过了这复杂难懂的思考,最后的代码如下:

#include<iostream>
#include<cstdio>
using namespace std;
int thing[1000001][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=thing[i][0];v<=V;v++)
		{
			thing[v][2]=max(thing[v][2],thing[v-thing[i][0]][2]+thing[i][1]);
		}
	cout<<thing[V][2];
	return 0;
}

欸哟,写一篇文章真累真舒服啊,请大家多多关注我的文章,有什么问题可以多多提出。

发布了8 篇原创文章 · 获赞 3 · 访问量 473

猜你喜欢

转载自blog.csdn.net/robotlongtime/article/details/105110257
今日推荐