完全背包问题

这篇博客探讨了完全背包问题的动态规划解决方案。通过三层循环的思想,博主阐述了如何考虑不同物品数量对总价值的影响。在想法2中,通过优化递推公式,将问题简化为一维数组,避免了多余的条件判断,提高了效率。核心在于利用已更新的f数组值不断尝试加入物品并更新最大价值。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目

在这里插入图片描述

想法1

采用三层循环,尝试第i种装k个的情况,只要第i中装k个的体积<j就比较采用前i个装j容量和采用前i-1个装j-k*v[i]容量价值+k*w[i]价值之和哪个比较大

#include<iostream>
using namespace std;
const int MAX=1005;
int w[MAX];
int v[MAX]; 
int f[MAX][MAX];
int max(int a,int b){
	if(a>b) return a;
	else return b; 
}
int main(){
	int N,V,j;
	cin>>N>>V;
	for(int i=1;i<=N;i++){
		cin>>v[i]>>w[i];
	}
	for(int i=1;i<=N;i++){
		for(j=1;j<=V;j++){
			for(int k=0;k*v[i]<=j;k++)
				f[i][j]=max(f[i][j],f[i-1][j-k*v[i]]+k*w[i]);
		}
	}
	cout<<f[N][V]<<endl;
} 

之前纠结:f[i][j]=max(f[i][j],…)还是=max(f[i-1][j]…),个人认为是都可以的,由于初始条件下f[i][j]为0,所以在第一次max比较的时候肯定会被修改为一个值。而f[i-1][j]对应的是k=0的情况,也就是在k=0的时候第一次被修改的情况,如果初始设为f[i-1][j]的话,k可以从1开始。

想法2(改进)

可以看出在想法1中采用三层循环。根据分析可知,
f [ i ][ j ] = max(f[ i-1 ][ j ],f[ i-1 ][ j-v [ i ] ]+w[ i ],f[ i-1 ][ j-2*v[ i ] ]+2*w[ i ],…)
f [ i ][ j-v[ i ] ]=max(f[ i-1 ][ j-v[ i ] ],f[ i-1 ][ j-2*v[ i ] ]+w[ i ],…)
所以,f[ i ][ j ]=max(f[ i -1 ][ j ],f[ i ][ j-v[ i ] ]+w[ i ])
f [ i ][ j-v[ i ] ]+w[ i ]这句话的意思是在当前的 i 作为一维的情况下,不断使用前面已经更新过的 f [ i ][ 前面体积 ]来更新 f [ i ][ 后面的体积 j ] 。当 j >= v [ i ] 的时候,就会尝试加入一个 i 物品,并与前面 i - 1 个物品的方案作比较,如果加入一个 i 物品的方案的价值大于原先同体积时的方案,那么就会更新这个体积 j 时的方案价值,所以如果后面的 j - v [ i ] 得到的体积所对应的最大价值是恰好先前已经更新过的体积 j ,那么这时候得到的方案就是在先前更新的方案中继续加入一个 i 物品(这样就实现了 i 物品的叠加操作),如果新的方案优于旧的方案,那么就更新 f 数组。如此循环.
关键点来了!01背包的递推式为:
f[ i ][ j ]=max(f[ i-1 ][ j ],f[ i-1 ][ j-v[ i ]+w [ i ])
所以可以参照01背包的化简方法,将其化简为一维数组:
f[j]=max(f[j],f[j-v[i]]+w[i])《与01背包表达式相同》:此时的j一定要从前向后更新!!

#include<iostream>
using namespace std;
const int MAX=1005;
int w[MAX];
int v[MAX]; 
int f[MAX];
int max(int a,int b){
	if(a>b) return a;
	else return b; 
}
int main(){
	int N,V,j;
	cin>>N>>V;
	for(int i=1;i<=N;i++){
		cin>>v[i]>>w[i];
	}
	for(int i=1;i<=N;i++){
		for(j=v[i];j<=V;j++){
			f[j]=max(f[j],f[j-v[i]]+w[i]);
		}
	}
	cout<<f[V]<<endl;
} 

小tips:此时在二层循环中省去了一个if条件:因为将j从v【i】开始,所以不用判断j啦~

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值