一.前言
我在前两篇博客中分别介绍了01背包以及完全背包问题,在这篇博客我主要想写一下自己关于多重背包问题的基本原理的见解。
背包问题是动态规划的一个巨大的分支,常见的背包问题都有相对的模版,个人认为如果只是会背板子是下下之策,从长远的角度来看是不可取的,因此我想在这里分享一些个人对于背包问题的理解(会有借鉴其他大牛地方,逃~)同时如果我有一些不正的确的地方也欢迎大家和我交流。希望能加深大家对背包问题的理解。
二.多重背包问题的原理以及常见例题
1.模版题引入:
有 N 种物品和一个容量是 V 的背包。
第 i种物品最多有 s[i]件,每件体积是 vi,价值是 wi。
求解将哪些物品装入背包,可使物品体积总和不超过背包容量,且价值总和最大。
输出最大价值。
输入格式
第一行两个整数,N,V,用空格隔开,分别表示物品种数和背包容积。
接下来有 N行,每行三个整数 v[i],w[i],s[i],用空格隔开,分别表示第 i 种物品的体积、价值和数量。
输出格式
输出一个整数,表示最大价值。
数据范围
0<N,V≤100
0<vi,wi,si≤100
输入样例
4 5
1 2 3
2 4 1
3 4 3
4 5 2
输出样例:
10
1.题目分析:
对于多重背包问题我们可以发现和完全背包似乎有点相似,实则不然。首先多重背包的选择方式与完全背包一模一样,但是可供选择的物品数量不一定相同。在完全背包问题中我们一定可以去选择取V/v[i]个第i种物品,而多重背包不一定有这么多件。我认为完全背包问题是特殊的多重背包问题,首先两者的朴素写法相同,其次当多重背包中的s[i]大于等于V/v[i]时,多重背包的优化与完全背包问题的一模一样,然而当s[i]小于V/v[i]时则不能采取等量变换的优化方式。故两者的优化会有所不同。
2.题解:
由于本题数据范围比较水,所以可以直接暴力求解。核心代码如下:
for(int i=1;i<=n;i++)
for(int j=0;j<=m;j++)
for(int k=0;k*v[i]<=j&&k<=s[i];k++)
f[i][j]=max(f[i][j],f[i-1][j-k*v[i]]+k*w[i]);
3.优化:
多重背包问题优化方式有两种,第一种是将多重背包问题通过二进制优化转换成01背包问题,这种方法也可以对完全背包问题进行优化,代码如下:
for(int i=1;i<=n;i++)
{
int a,b,c;
cin>>a>>b>>c;
int k=1;
while(c>=k)
{
c-=k;
cnt++;
v[cnt]=k*a;
w[cnt]=k*b;
k*=2;
}
if(c)
{
cnt++;
v[cnt]=c*a;
w[cnt]=c*b;
}
}
第二种优化方式是单调队列优化,感兴趣的话可以去了解“男人八题”,笔者能力有限就不展开讲了;
2.典型例题:
例题一、 庆功会
为了庆贺班级在校运动会上取得全校第一名成绩,班主任决定开一场庆功会,为此拨款购买奖品犒劳运动员。
期望拨款金额能购买最大价值的奖品,可以补充他们的精力和体力。
输入格式
第一行二个数n,m,其中n代表希望购买的奖品的种数,m表示拨款金额。
接下来n行,每行3个数,v、w、s,分别表示第I种奖品的价格、价值(价格与价值是不同的概念)和能购买的最大数量(买0件到s件均可)。
输出格式
一行:一个数,表示此次购买能获得的最大的价值(注意!不是价格)。
数据范围
n≤500,m≤6000,
v≤100,w≤1000,s≤10,
输入样例:
5 1000
80 20 4
40 50 9
30 50 7
40 30 6
20 20 1
输出样例:
1040
题目分析:
简单的模版题带入实际意义即可,本题数据比较少有多种方法解决;
ac代码(朴素版本):
#include<iostream>
using namespace std;
const int N=6010,M=510;
int f[M][N],v[N],w[N],s[N];
int main()
{
int n,m;
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=0;j<=m;j++)
{
for(int k=0;k*v[i]<=j&&k<=s[i];k++)
{
f[i][j]=max(f[i][j],f[i-1][j-k*v[i]]+k*w[i]);
}
}
}
cout<<f[n][m]<<endl;
return 0;
}
ac代码(二进制优化):
#include<iostream>
using namespace std;
const int N=100100;
int f[N],v[N],w[N];
int main()
{
int n,m;
cin>>n>>m;
int cnt=0;
for(int i=1;i<=n;i++)
{
int a,b,c;
cin>>a>>b>>c;
int k=1;
while(c>=k)
{
c-=k;
cnt++;
v[cnt]=k*a;
w[cnt]=k*b;
k*=2;
}
if(c)
{
cnt++;
v[cnt]=c*a;
w[cnt]=c*b;
}
}
for(int i=1;i<=cnt;i++)
{
for(int j=m;j>=0;j--)
{
if(j>=v[i])f[j]=max(f[j],f[j-v[i]]+w[i]);
}
}
cout<<f[m]<<endl;
return 0;
}
三.小结
多重背包问题与01背包和完全背包问题有着十分紧密的联系,我们在写题的时候要加以辨别。