完全背包问题

时间限制: 1Sec 内存限制: 128MB 提交: 555 解决: 238

题目描述

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

输入

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

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

输出

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

样例输入

10 4
2 1
3 3
4 5
7 9

样例输出

max=12

        思路:01背包问题进阶,不同之处在于01背包问题只有选和不选两个种可能,而完全背包问题需要考虑选几个的问题,简单的思路是在01背包问题的基础上,通过增加一重循环控制能放入的数量,01背包因为只需考虑放入和不放入,所以用判断就可以 ,代码如下:

#include <bits/stdc++.h>
using namespace std;
int dp[205];
int w[35],c[35];
int main() {
	int n,m;
	cin >> m >> n;
	for(int i=1;i<=n;i++)
		scanf("%d%d",&w[i],&c[i]);
	for(int i=1;i<=n;i++) {
		for(int j=m;j>=0;j--)
			for(int k=0;k<=j/w[i];k++) //此处将判断改为循环,其实已经对当前背包能放下多少当前物品进行控制 
				dp[j] = max(dp[j],dp[j-k*w[i]]+k*c[i]); 
	} 
	cout << "max=" << dp[m];
	return 0;
}

        这段代码可以AC本题,但是这并不是完全背包问题的最优解,三重循环的设计大大增加了时间复杂度。

        回顾01背包问题,本次决策只与上一轮小于等于该当前背包容量的结果有关,用到的是来自上一轮的旧数据,在优化时为了防止数据被覆盖,内层循环遍历时是从背包的最大容量开始的,优化前后的状态转移方程如下动态转移方程如下:

dp[ i ][ j ] = max( dp[ i-1 ][ j ] , dp[ i-1 ][ j-w[i] ]+c[i] )

dp[ j ] = max( dp[ j ] , dp[ j-w[i] ]+c[ j ] )

        对于完全背包问题,在考虑时,数据并不会完全来自上轮的旧数据,例如在同一大小的背包下,如果(当前这个物品放两个的价值)高于(放1个当前物品加上上一轮同等规模背包的价值总和),我们显然会两个当前物品,所以,这一轮的数据有可能不来自上一轮,当当前物品的性价比较高的时候,可能会放多个当前物品,而内层循环背包的容量是逐渐增大的,当前物品的个数也是逐渐增大的,所以当前数据可能来自当这一轮更新的数据。打表如图:

        箭头就是数据的来源,值得注意的是,当前物品重量为3,背包重量为5时,放入一个重量为2的物品和一个重量为3的物品是最优解,它的来源也是当前新更新的数据。所以可以写出状态转移方程如下:

dp[ i ][ j ] = max( dp[ i-1 ][ j ] , dp[ i ][ j-w[i] ]+c[i] 

代码如下:

#include <bits/stdc++.h>
using namespace std;
int dp[205][205];//横坐标表示当前背包体积,纵坐标表示当前物品重量,dp值表示当前总价值 
int w[35],c[35];
int main() {
	int n,m;
	cin >> m >> n;
	for(int i=1;i<=n;i++)
		scanf("%d%d",&w[i],&c[i]);
	for(int i=1;i<=n;i++)
		for(int j=1;j<=m;j++)
			if(j<w[i]) dp[i][j] = dp[i-1][j];
			else dp[i][j] = max(dp[i-1][j],dp[i][j-w[i]]+c[i]); 
//	for(int i=0;i<=n;i++) {
//		for(int j=0;j<=m;j++)
//			printf("%d ",dp[i][j]);
//		printf("\n");	
//	}
	cout << "max=" << dp[n][m];
	return 0;
}

        注释部分为打表,和手写的完全相同。

        与01背包一样,可以进行同样的优化,状态转移方程为:

dp[ j ] = max( dp[ j ] , dp[ j-w[i] ]+c[ j ] )

 代码如下:

#include <bits/stdc++.h>
using namespace std;
int dp[205];
int w[35],c[35];
int main() {
	int n,m;
	cin >> m >> n;
	for(int i=1;i<=n;i++)
		scanf("%d%d",&w[i],&c[i]);
	for(int i=1;i<=n;i++) {
		for(int j=1;j<=m;j++)
			if(j>=w[i]) 
				dp[j] = max(dp[j],dp[j-w[i]]+c[i]); 
	} 
	cout << "max=" << dp[m];
	return 0;
} 

题目来源:题目 2132: 信息学奥赛一本通T1268-完全背包问题

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值