10.14学习内容(动态规划第二节背包)

这篇博客介绍了动态规划在解决背包问题中的应用,包括01背包、完全背包和多重背包。对于01背包问题,解释了基本思路和状态转移方程,并给出了代码优化方案。完全背包问题与01背包类似,但允许无限选取同种物品。多重背包问题引入了每种物品有限数量的情况,提出了转化成01背包的优化方法。

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

例题1:01背包

1267:【例9.11】01背包问题


时间限制: 1000 ms         内存限制: 65536 KB
提交数: 34747     通过数: 20968

【题目描述】

一个旅行者有一个最多能装 MM 公斤的背包,现在有 nn 件物品,它们的重量分别是W1,W2,...,WnW1,W2,...,Wn,它们的价值分别为C1,C2,...,CnC1,C2,...,Cn,求旅行者能获得最大总价值。

【输入】

第一行:两个整数,MM(背包容量,M<=200M<=200)和NN(物品数量,N<=30N<=30);

第2..N+12..N+1行:每行二个整数Wi,CiWi,Ci,表示每个物品的重量和价值。

【输出】

仅一行,一个数,表示最大总价值。

【输入样例】

10 4
2 1
3 3
4 5
7 9

【输出样例】

12

【题目解析】

基本思路:

状态数组的定义:

dp[i][j],表示选第i个物品时,在j空间下所获取的最大价值是多少。

阶段:

以物品为阶段,1—n个物品。

决策:

即在第i个物品时,选或不选。

边界条件:

因为要取最大值,把dp数组定义为全局变量,即为0.

状态转移方程:

dp[i][j]=max(dp[i][j],dp[i-1][j-v[i]]+w[i]); dp[i][j]继承的是dp[i-1][j]的值,在目前空间大于目前所选物品所需要空间的情况下

代码参考:

#include<iostream>
using namespace std;
int v[210],w[35];
int dp[35][210];
int main(){
	int m,n;
	cin>>m>>n;
	for(int i=1;i<=n;i++){
		cin>>v[i]>>w[i];
	}
	for(int i=1;i<=n;i++){
		for(int j=0;j<=m;j++){
			dp[i][j]=dp[i-1][j];
			if(j>=v[i]){
				dp[i][j]=max(dp[i][j],dp[i-1][j-v[i]]+w[i]);
			}
		}
	}
	cout<<dp[n][m];
	return 0;
} 

代码可以再次进行优化,用一维的dp数组进行操作,只需要保证dp[j]能够继承到dp[j-v[i]]的值,由此可以将第二层for循环倒叙枚举空间即可。

【代码参考】

#include<iostream>
using namespace std;
int m,n;
int w[35],v[35];
int dp[210];
int main(){
	cin>>m>>n;
	for(int i=1;i<=n;i++){
		cin>>v[i]>>w[i];
	}
	for(int i=1;i<=n;i++){
		for(int j=m;j>=v[i];j--){
			dp[j]=max(dp[j],dp[j-v[i]]+w[i]);
		}
	}
	cout<<dp[m];
	return 0;
}

例题2:完全背包

1268:【例9.12】完全背包问题


时间限制: 1000 ms         内存限制: 65536 KB
提交数: 28446     通过数: 15243

【题目描述】

设有nn种物品,每种物品有一个重量及一个价值。但每种物品的数量是无限的,同时有一个背包,最大载重量为MM,今从nn种物品中选取若干件(同一种物品可以多次选取),使其重量的和小于等于MM,而价值的和为最大。

【输入】

第一行:两个整数,MM(背包容量,M≤200M≤200)和NN(物品数量,N≤30N≤30);

第2..N+12..N+1行:每行二个整数Wi,CiWi,Ci,表示每个物品的重量和价值。

【输出】

仅一行,一个数,表示最大总价值。

【输入样例】

10 4
2 1
3 3
4 5
7 9

【输出样例】

max=12

题目解析:

做法思路大体与01背包相似,只需向内再加一层for循环枚举第i间物品在当前空间内可以取到的件数即可。

【代码参考】

#include<iostream>
using namespace std;
int m,n;
int v[35],w[35]; 
int dp[35][210];
int main(){
	cin>>m>>n;
	for(int i=1;i<=n;i++){
		cin>>v[i]>>w[i];
	}
	for(int i=1;i<=n;i++){
		for(int j=0;j<=m;j++){
			dp[i][j]=dp[i-1][j];
			for(int k=1;k<=m/v[i];k++){
				if(j>=k*v[i]){
					dp[i][j]=max(dp[i][j],dp[i-1][j-k*v[i]]+k*w[i]);	
				}
				
			}
		}
	}
	cout<<"max="<<dp[n][m];
	return 0;
} 

同样也有一维优化,第二层for循环从v[i]开始,枚举到所能承受的最大空间,每次d[j]都继承d[j-v[i]]的值即可。

【代码参考】

#include<iostream>
using namespace std;
int v[210],w[35];
int dp[210];
int main(){
	int m,n;
	cin>>m>>n;
	for(int i=1;i<=n;i++){
		cin>>v[i]>>w[i];
	}
	for(int i=1;i<=n;i++){
		for(int j=v[i];j<=m;j++){
			dp[j]=max(dp[j],dp[j-v[i]]+w[i]);
		}
	}
	cout<<"max="<<dp[m];
	return 0;
} 

例题3:多重背包

4. 多重背包问题 I

有 NN 种物品和一个容量是 VV 的背包。

第 ii 种物品最多有 sisi 件,每件体积是 vivi,价值是 wiwi。

求解将哪些物品装入背包,可使物品体积总和不超过背包容量,且价值总和最大。
输出最大价值。

输入格式

第一行两个整数,N,VN,V,用空格隔开,分别表示物品种数和背包容积。

接下来有 NN 行,每行三个整数 vi,wi,sivi,wi,si,用空格隔开,分别表示第 ii 种物品的体积、价值和数量。

输出格式

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

数据范围

0<N,V≤1000<N,V≤100
0<vi,wi,si≤1000<vi,wi,si≤100

输入样例

4 5
1 2 3
2 4 1
3 4 3
4 5 2

输出样例:

10

【题目解析】

相对于完全背包来说,每件物品可以选择的数量是有限的,做法与完全背包相似。

【代码参考】(朴素版本)

#include<iostream>
using namespace std;
const int N=110;
int m,n;
int v[N],w[N],dp[N][N],s[N];
int main(){
	cin>>n>>m;
	for(int i=1;i<=n;i++){
		cin>>v[i]>>w[i]>>s[i];
	}
	for(int i=1;i<=n;i++){
		for(int j=1;j<=m;j++){
			dp[i][j]=dp[i-1][j];
			for(int k=1;k<=s[i];k++){
				if(k*v[i]<=j){
					dp[i][j]=max(dp[i][j],dp[i-1][j-k*v[i]]+k*w[i]);
				}
			}
		}
	}
	cout<<dp[n][m];
	return 0;
}

也可以按以上方法进行一维优化

【代码参考】

#include<iostream>
using namespace std;
const int N=1100;
int m,n;
int v[N],w[N],dp[N],s[N];
int main(){
	cin>>n>>m;
	for(int i=1;i<=n;i++){
		cin>>v[i]>>w[i]>>s[i];
	}
	for(int i=1;i<=n;i++){
		for(int j=m;j>=v[i];j--){
			for(int k=1;k<=s[i];k++){
				if(k*v[i]<=j){
					dp[j]=max(dp[j],dp[j-k*v[i]]+k*w[i]);
				}
			}
		}
	}
	cout<<dp[m];
	return 0;
}

当然,也可以将多重背包转化为01背包,进行二进制优化,如:

当输入第i行时数据为3    5    7时,表示此物品每件的体积为3,价值为5,数量为7,

则可将7拆分成

1  2  4

3   5   1

6  10  2

12  20  3

将其分别存储起来,按照解01背包的方法进行处理,即可达到优化运算的效果。

【代码参考】

#include<iostream>
using namespace std;
const int N=2e5+5;
int m,n;
int v[N],w[N],dp[N];
int main(){
	cin>>n>>m;
	int l=0;
	for(int i=1;i<=n;i++){
		int a,b,c;
		cin>>a>>b>>c;
		int k=1;
		while(c>=k){
			l++;
			v[l]=a*k;
			w[l]=b*k;
			c=c-k;
			k+k*2;
		}
		if(c>0){
			v[++l]=a*c;
			w[l]=b*c;
		}
	}
	for(int i=1;i<=l;i++){
		for(int j=m;j>=v[i];j--){
			dp[j]=max(dp[j],dp[j-v[i]]+w[i]);
		}
	}
	cout<<dp[m];
	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值