完全背包问题

前言:这个问题是建立在01背包的基础之上的,如果没听过01背包的小伙伴可以先前往我的上一篇文章中进行学习。接下来要学习的是背包系列中的完全背包

完全背包同样也是动态规划算法中很经典的模板题,题目描述如下:

有 N 种物品和一个容量是 V 的背包,每种物品都有无限件可用。
第 i 种物品的体积是 vi,价值是 wi。
求解将哪些物品装入背包,可使这些物品的总体积不超过背包容量,且总价值最大。
输出最大价值。


输入格式
第一行两个整数,n,m,用空格隔开,分别表示物品种数和背包容积。
接下来有 n 行,每行两个整数 v[i],w[i],用空格隔开,分别表示第 i 种物品的体积和价值。
输出格式
输出一个整数,表示最大价值。
数据范围
0<n,m≤1000
0<vi,wi≤1000


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

这里很明显可以发现,01背包的代码已经不能解决这个问题,那我们先考虑,我们面对每一种物品时的选择:选/不选,选几个?

想到这里,我们就可以结合01背包的代码,得到完全背包的朴素算法,代码如下:

#include<bits/stdc++.h>
using namespace std;
int f[1005],v[1005],w[1005],n,m;
int main(){
	int i,j,k;
	cin>>n>>m;
	for(i=1;i<=n;i++){
		cin>>v[i]>>w[i];
	}
	for(i=1;i<=n;i++){//遍历n个物品 
		for(j=n;j>=v[i];j--){//倒着枚举体积 
			for(k=1;k*v[i]<=j;k++){//枚举选几个 
				f[j]=max(f[j],f[j-v[i]*k]+w[i]*k);
				//状态转移(第i种物品要占用k*v[i]的体积,产生k*w[i]的价值) 
			}
		}
	} 
	cout<<f[m]; 
	return 0;
}

但是看到数据范围,再加上我们要处理实际问题,肯定是不能用三重循环的,这时候就要想办法精简我们的代码,提升算法效率。我们先写下状态转移方程:

f[j]=max(f[j],f[j-v[i]]+w[i],f[j-2*v[i]]+2*w[i],......,f[j-k*v[i]]+k*w[i]);

完成一次操作f[j]=max(f[j],f[j-v[i]]+w[i])后方程变为:
f[j]=max(f[j],f[j-v[i]]+w[i],f[j-2*v[i]]+2*w[i],......,f[j-(k-1)*v[i]]+(k-1)*w[i]);

再完成一次操作f[j]=max(f[j],f[j-v[i]]+w[i])后方程变为:
f[j]=max(f[j],f[j-v[i]]+w[i],f[j-2*v[i]]+2*w[i],......,f[j-(k-2)*v[i]]+(k-2)*w[i]);

再完成一次操作f[j]=max(f[j],f[j-v[i]]+w[i])后方程变为:
f[j]=max(f[j],f[j-v[i]]+w[i],f[j-2*v[i]]+2*w[i],......,f[j-(k-3)*v[i]]+(k-3)*w[i]);

以此类推,最终状态转移方程又会变成f[j]=max(f[j],f[j-v[i]]+w[i]);

由此我们可以得到,只要把01背包中的枚举体积改为正序即可完成完全背包问题,代码如下:

#include<bits/stdc++.h>
using namespace std;
int f[1005],v[1005],w[1005],n,m;
int main(){
	int i,j,k;
	cin>>n>>m;
	for(i=1;i<=n;i++){
		cin>>v[i]>>w[i];
	}
	for(i=1;i<=n;i++){//遍历n种物品 
		for(j=v[i];j<=m;j++){//正着枚举物品总体积 
			f[j]=max(f[j],f[j-v[i]]+w[i]);//状态转移方程 
		}
	}
	cout<<f[m]; 
	return 0;
}

如果学懂了的小伙伴们可以尝试一下洛谷P1616 采药(几乎纯模板):

疯狂的采药 - 洛谷icon-default.png?t=N7T8https://www.luogu.com.cn/problem/P1616最后希望大家能够喜欢我的文章,多多支持我,有问题的小伙伴可以私信交流,大家共同进步! 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值