多重背包问题

我们前面已经讲了01背包和完全背包问题,其实多重背包就是由01背包引申而来的,所以没看过01背包问题的建议先去看看。
01背包二维数组
01背包一维数组
另外解决多重背包问题,学会完全背包也是个关键。
完全背包问题之二维数组解法
完全背包问题之一维数组解法
由于多重背包的解法有很多,这次就先讲用“朴素算法”求解。
题目如下:

【题目介绍】

【题目描述】

为了庆贺班级在校运动会上取得全校第一名成绩,班主任决定开一场庆功会,为此拨款购买奖品犒劳运动员。期望拨款金额能购买最大价值的奖品,可以补充他们的精力和体力。

【输入】

第一行二个数 n n n( n ≤ 500 n\le500 n500), m m m( m ≤ 6000 m\le6000 m6000),其中 n n n代表希望购买的奖品的种数, m m m表示拨款金额。

接下来n行,每行3个数, v 、 w 、 s v、w、s vws,分别表示第I种奖品的价格、价值(价格与价值是不同的概念)和能购买的最大数量(买0件到s件均可),其中 v ≤ 100 , w ≤ 1000 , s ≤ 10 v\le100,w\le1000,s\le10 v100w1000s10

【输出】

一行:一个数,表示此次购买能获得的最大的价值(注意!不是价格)。

【输入样例】

5 1000
80 20 4
40 50 9
30 50 7
40 30 6
20 20 1

【输出样例】

1040

【数据储存】

和01背包,完全背包问题基本一样,物品数量和背包容量分别用两个变量储存,每个物品的重量、价值和数量都用结构体储存即可。

【算法分析】

和01背包、完全背包一样,都用动态规划算法解决。

【dp数组含义】

其实01背包、完全背包和多重背包问题中dp数组的含义都是一样的,二维dp数组都是指物品 i 放进背包容量为 j 的背包里的最大价值。

【填表】

这次为什么要先把这个放到【递推公式】前面呢?因为多重背包问题要想明白递推公式,要先知道它是如何填表的。
多重背包的循环顺序其实和01背包、完全背包差不多,第一层循环循环物品,第二层循环循环背包,但不同的是,要有第三层循环,用来循环物品数量。
故循环顺序为:

for(int i=2;i<=n;i++){
	for(int j=1;j<=m;j++){
		for(int k=0;k<=s[i].s&&k*s[i].w<=j;k++){
			递推公式
			}
	}
}

【递推公式】

虽然含义一样,但是递推公式还是有一些不同的。
01背包的是:
当 j < s[i].w 时,dp[i][j]=dp[i-1][j];
否则,dp[i][j]=max(dp[i-1][j],dp[i-1][j-s[i].w]+s[i].v);
完全背包的是:
当 j < s[i].w 时,dp[i][j]=dp[i-1][j];
否则,dp[i][j]=max(dp[i-1][j],dp[i][j-s[i].w]+s[i].v);
但是,由于还要加上每种情况下对物品数量的循环,所以应改为:
当 j < s[i].w 时,dp[i][j]=dp[i-1][j];
否则,dp[i][j]=max(dp[i-1][j],dp[i-1][j-ks[i].w]+ks[i].v);

【dp数组初始化】

第0列指的是将物品 i 装进背包容量为 0 的背包里,很明显装不了,所以这一列都为0.
第一行其实和完全背包的初始化差不多,但是比完全背包要多一个条件,那就是数量不能超过本物品所有的数量,若超过,则dp[1][i]=dp[1][i-1];否则,dp[1][i]=i/s[i].w*s[i].v;
最后附上AC代码:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int maxn = 15000;
int dp[maxn][maxn];
struct ss{//利用结构体储存数据
	int v,w,s;
}ss1[maxn];//避免重名
int main(){
    int n,m;
    cin>>n>>m;//表示物品数量和背包容量
    for(int i=1;i<=n;i++)cin>>ss1[i].w>>ss1[i].v>>ss1[i].s;
    for(int i=1;i<=n;i++){//初始化第0列
    	dp[i][0]=0;
	}
	for(int i=0;i<=m;i++){//初始化第一行
		if(i>ss1[1].w ){
			if(i/ss1[1].w<=ss1[1].s){
				dp[1][i]=i/ss1[1].w*ss1[1].v;
			}
			else{
				dp[1][i]=dp[1][i-1];
			}
		}
		else{
			dp[1][i]=0;
		}
	}
    for(int i=2;i<=n;i++)//填表
        for(int j=1;j<=m;j++)
            for(int k=0;k<=ss1[i].s&&k*ss1[i].w<=j;k++)
                dp[i][j]=max(dp[i][j],dp[i-1][j-k*ss1[i].w]+k*ss1[i].v);
    cout <<dp[n][m]<< endl;
    return 0;
}

从【填表】这里我们可以发现,“朴素算法”需要三次for循环,若数据范围到达1000时,就会超时。
所以需要优化。
下期预告:用二进制优化多重背包问题

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值