九大背包问题专题--完全背包问题(详解,最优解)

2.完全背包问题

和01背包问题的区别:
01背包问题:1件物品只能选或者不选

完全背包问题:1件物品可以重复选多次,只要不超过总体积

题目:
问题:
有N件物品和一个容量是V的背包。
第i件物品的体积是vi,价值是wi。
求解将哪些物品装入背包,可使这些物品的总体积不超过背包的容量,且价值最大。

输入格式
第一行有两个整数,N,V用空格隔开,分别表示物品数量和背包容积。
接下来有N行,每行两个整数vi,wi,用空格隔开,分别表示第i件物品的体积和价值。

输出格式
输出一个整数,表示最大价值

数据范围
0<N.V<=1000
0<vi,wi<=1000

输入样例
4 5
1 2
2 4
3 4
4 5

输出样例
10

分析思路:

f[i]:表示总体积是i的情况下,最大价值是多少

f[0…m]枚举,m表示背包容量
result=max{f[0…m]}

01背包从大到小枚举->保证每个物品只用1次

从前往后考虑
for(int i=0;i<n;i++){
    for(int j=m,j>=v[i];j--)
       f[j]=max(f[j],f[j-v[i]]+w[i])
原因:保证状态转移时,算f[j]时,算的是f[i][j](第i个物品的j),保证转移时,用的是f[i-1][j]
f[j]=max(f[j],f[i-1][j-v[i]]+w[i]);
而不是 f[j]=max(f[j,]f[i][j-v[i]]+w[i])
从大到小枚举,v[i]>0,[j-v[i]]一定没有被算过,则它一定是f[i-1]的状态。

完全背包从小到大枚举->保证在背包容量允许的条件下,每个物品可重复多次使用


现在枚举第i个物品,体积从大到小枚举
 for(int j=m,j>=v[i];j--)
   for(int k=0;k*v[i]<=j;k++)  //选的物品数
      f[j]=max(f[j],f[j-k*v[i]]+k*w[i])  //一个物品重复选多次,直到装不下,一定不包含当前的第i个物品
算f[j]时,用来转移的状态一定是f[i-1]的

算f[j]时,所有比j小的f[j]都没有被算过,上面所有的f[j-k*v[i]都没算过,都不包含第i个物品

枚举选k个,对不同的k,更新刚才的状态,体积就是j-k*v[i]],价值就是k*w[i]

从小到大枚举,在算f[j]时,用j-v[i]这个状态,j-v[i]这个状态是被算过的,
因为从小到大枚举,所以算f[j]时,比j小的都已经算过了,j-v[i]也在里面,所以j-v[i]这个状态也被算过了

f[j-v[i]]表示考虑前i个物品,包括第i个物品的情况下,体积是j-v[i]时,最大价值是多少,可能已包含若干的当前第i个的物品。

综上,从小到大更新状态,f[j-v[i]]包含第i个物品,证明从小到大枚举能枚举到最优解.

证明:数学归纳法(迭代,递推关系)

1.假设前i-1个物品考虑完之后,所有的f[j]都是符合的,所有f[j]表示体积是j的情况下,它的最大价值就是f[j]
初始化:f[0][0]正确

2.考虑完第i个物品后,所有f[j]也是符合的

对某一个j而言,把j确定,最优解里面包含k个v[i],第i个物品

从小到大枚举,一定可以枚举到f[j-k*v[i]]状态,算此状态时,
用f[j-k*v[i]-v[i]]+w[i]更新状态(此最优解不包含v[i]),

反之f[j-k*v[i]]不包含,所以不会更新,取完max后一定是原来的数
f[j-k*v[i]]此状态已算完

接着算(k-1)*v[i]状态:f[j-(k-1)*v[i]]会用f[j-v[i]]+w[i])更新它,更新之后变为:f[j-k*v[i]-v[i]]+w[i]

综上:f[j-(k-1)*v[i]]这个状态会用f[j-k*v[i]](一个v[i]都不包含)这个状态更新;枚举完一个之后就会包含一个v[i],
依次类推:算f[j]时,用f[j-v[i]]+w[i])(此状态一定计算过只包含k-1个物品这个状态)更新,所以说f[j]这个物品枚举了包含k个v[i]

如果最后结果中包含k个v[i],那么f[j]就一定枚举到过这种状态
所以f[j]就一定会枚举到最优解

举例:
1、前1个物品中:f[1]=2,f[2]=4,f[3]=6,f[4]=8,f[5]=10,显然f[j]都是正确的。
2、假设在前i-1个物品中,f[j]都是正确的。
3、在前i个物品中,对于某个j而言,如果最优解包含k个v[i],则一定会枚举到f[j-kv[i]],f[j-kv[i]]是如何得到的呢?
f[j-kv[i]=max{f[j-kv[i]],f[j-kv[i]-v[i]]+w[i]} (由于j正序,f[j-kv[i]]为前i-1个物品的值,f[j-k*v[i]-v[i]]为前i个物品的值).
最后会传递到max{f[v[i]],f[0]+w[i]}处。

因为f[v[i]]一定正确(2中已假设前i-1个物品中的f[j]全都正确),f[0]一定正确=0,w[i]一定正确,所max{f[v[i]],f[0]+w[i]}
一定正确=>f[j-k*v[i]]一定正确=>f[j]一定正确.

时间复杂度:1000000
代码:

#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;

const int N=1010;
int n,m; 
int f[N];
int main(){
	cin>>n>>m;
	for(int i=0;i<n;i++){  //从0开始枚举 
		int v,w;
		cin>>v>>w;
		for(int j=v;j<=m;j++) //小到大枚举体积,<v的不枚举 
		  f[j]=max(f[j],f[j-v]+w);
	} 
	/*初始化的时候把所有的 f[i]都初始化为0 
	/f[m] 表示体积 小于等于m的情况下 ,所有方法里面 
	/转移的时候不一定是从f[0]转移过来的,可以从任意一个状态转移 
	/当用不完整个背包的容量的时候 ,假设剩k容量就可以从k转移 
	/则一定可以枚举到 最优解一样的选法 ,因为体积变成了一样的 
	/假设最优解用了m-k的体积,f[m]就一定可以从k开始枚举 ,也只用了 m-k的体积
	 也会枚举到最优解 ,所以f[m]表示的就是体积小于等于 m的情况下的最优解 */ 
	cout<<f[m]<<endl;  //最大价值(不需要枚举从0到m) 
	return 0;
}

在这里插入图片描述
若题目问体积恰好为m的情况下,最大价值为多少?
除f[0]=0
在初始化时所有f[i]初始化为负无穷

  • 17
    点赞
  • 96
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值