目录
背包问题都是由01背包延申出来的,在01背包基础上加一些限制条件。只用01背包的思想理解了,其它背包就很容易理解。
本文以最经典的背包容量、物品重量、价值为例,对几类背包问题做了分析。实际解题过程中,题目描述不仅只限于背包可能是(1)预算总量、物品价格、物品重要程度、(2)食物体积限制、食物体积、食物卡路里 等。均可归纳为经典背包问题。
01背包
问题描述:容量为M的背包,有N个物品,重量为W1,W2…WN对应的价值分别为V1,V2,….VN。
目标:从N种物品种取若干件(每个物品只能选一次),使其重量的和小于等于M,而背包价值最大
特点:物品只能选一次
输入样例:
10 4
2 1
3 3
4 5
7 9
输出样例:
12
背包容量M=10,物品数量N=4,N+1行,每行两个整数W、 V,表示物品的重量和价值
状态转移方程-二维:
dp[i][j] = max(dp[i-1][j], dp[i-1][j-w[i]] + v[i])
dp[i][j]:选择i个物品,背包容量为j时,背包的最大价值。
max(dp [i-1][j-第i物品价值] +第i种物品价值,dp [i-1][j])
状态转移方程-一维:
dp [j] = max(dp [j], dp [j - w[i]] + v[i]);
01背包文件详解:动态规划_01背包问题-CSDN博客
完全背包
问题描述:容量为M的背包,有N个物品,重量为W1,W2…WN对应的价值分别为V1,V2,….VN。
目标:从N种物品种取若干件(同一物品可选无限次),使其重量的和小于等于M,而背包价值最大
特点:物品可以选无限次
输入样例:
10 4
2 1
3 3
4 5
7 9
输出样例:
max=12
背包容量M=10,物品数量N=4,N+1行,每行两个整数W、 V,表示物品的重量和价值
状态转移方程:
dp[i][j] = max(dp[i-1][j], dp[i][j-w[i]] + v[i])
dp[i][j]:选择i个物品,背包容量为j时,背包的最大价值。
max(dp [i][j-第i物品价值] +第i种物品价值,dp [i-1][j])
完全背包文件详解:动态规划_完全背包问题_动态规划求解完全背包问题-CSDN博客
多重背包
问题描述:容量为M的背包,有N个物品,重量为W1,W2…WN对应的价值分别为V1,V2,….VN,最多能选择的个数
目标:从N种物品种取若干件(同一物品可选有限个),使其重量的和小于等于M,而背包价值最大
特点:物品可以选有限个
输入样例:
5 12
8 2 4
4 5 9
3 5 2
4 3 6
2 2 1
输出样例:
17
状态转移方程:
dp[j]=max(dp[j],dp[j-k*w[i]]+k*v[i]) ;
dp[j]:背包容量为j时,背包的最大价值。
max(dp [j-第i物品选择k次的重量] +第i物品选择k次的价值,dp [j])
代码实现:
#include <bits/stdc++.h>
using namespace std;
int n,m,w[501],v[501],s[501],dp[6001];
int main(){
cin >> n >> m;
for(int i=1;i<=n;i++)
{
cin>>w[i]>>v[i]>>s[i];//重量、价值、个数
}
for(int i=1;i<=n;i++) //物品种类
{
for(int j=m;j>=w[i];j--) //价值
{
for (int k=1;k<=s[i];k++) //第i个物品最多能选择的个数
{
if(j>=k*w[i])
{
dp[j]=max(dp[j],dp[j-k*w[i]]+k*v[i]) ;
}
}
}
}
cout<<dp[m];
return 0;
}
分组背包
问题描述:容量为M的背包,有N个物品,重量为W1,W2…WN对应的价值分别为V1,V2,….VN。N个物品分成K组,每组中的物品相互冲突
特点:每组物品只能选一个
目标:从N种物品种取若干件(每个物品只能选一次),使其重量的和小于等于M,而背包价值最大
输入样例:
45 3
10 10 1
10 5 1
50 400 2
输出样例:
10
背包容量M=45,物品数量N=4,N+1行,每行三个整数表示物品的重量、价值、分组
状态转移方程:
dp[j]=max(dp[j],dp[j-w[wp]]+v[wp]);
dp[j]:背包容量为j时,背包的最大价值。
max(dp [j-第wp个物品重量] +第wp个物品价值,dp [j])
代码实现:
#include <bits/stdc++.h>
using namespace std;
//f[i][j] 小组i中第j个物品的编号
//b[i] 第i个小组的标号
//w[i] 第i个物品重量
//v[i] 第i个物品价值
//dp[j] 背包容量为j时,最大价值
int n,m ,team=0,w[1001],v[1001],b[101],f[1001][1001],dp[1001];
int main(){
cin >>m>> n;
for(int i=1;i<=n;i++)
{
int c;
cin>>w[i]>>v[i]>>c;
team=max(team,c);
b[c]++;
f[c][b[c]]=i;//第c个小组,第b[c]个编号,至为第i个物品
}
//按小组遍历,每个小组取一个
for(int i=1 ;i<=team;i++)
{
for(int j=m ;j>=0;j--)//容量
{
for(int k=1;k<=b[i];k++)//每个小组中的物品
{
int wp=f[i][k];//第i个小组,第k个编号保存的物品序号
if (j>=w[wp])
{
dp[j]=max(dp[j],dp[j-w[wp]]+v[wp]);
}
}
}
}
cout <<dp[m];
return 0;
}
有依赖的背包
问题描述:容量为M的背包,有N个物品,重量为W1,W2…WN对应的价值分别为V1,V2,….VN。物品N2,N3从属于N1,如果要选N2或N3必须要先选N1。N1可以有0个、1个或2个附件。每个附件对应一个主件,附件不再有从属于自己的附件
特点:附件不能单独选择
目标:从N种物品种取若干件(每个物品只能选一次),使其重量的和小于等于M,而背包价值最大
输入样例:
1000 5
800 2 0
400 5 1
300 5 1
400 3 0
500 2 0
输出样例:
2200
背包容量M=1000,物品数量N=5,N+1行,每行三个整数表示物品的重量、价值、分组
状态转移方程:
有附件的背包,也是01背包的变式。只是考虑的问题多了附加条件:
(1)不选
(2)只选主件,不选附件
dp[j]=max(dp[j],dp[j-v[i][0]]+w[i][0]);
(3)主件+附件1
dp[j]=max(dp[j],dp[j-v[i][0]-v[i][1]]+w[i][0]+w[i][1]);
(4)主件+附件2
dp[j]=max(dp[j],dp[j-v[i][0]-v[i][2]]+w[i][0]+w[i][2]);
(5)主件+附件1+附件2
dp[j]=max(dp[j],dp[j-v[i][0]-v[i][1]-v[i][2]]+w[i][0]+w[i][1]+w[i][2]);
代码实现:
#include <bits/stdc++.h>
using namespace std;
int n ,m ,dp[32001],v[61][3],w[61][3];
int main(){
scanf("%d%d",&m,&n);
for(int i=1; i<=n; i++)
{
int x,y,z;
scanf("%d%d%d",&x,&y,&z);
if(z==0)//主件
{
v[i][0]=x;
w[i][0]=x*y;
}
else//附件
{
if (v[z][1]==0 )
{
v[z][1]=x;
w[z][1]=x*y;
}
else if (v[z][2]==0 )
{
v[z][2]=x;
w[z][2]=x*y;
}
}
}
for(int i=1;i<=n;i++) //物品
{
for(int j=m; j>=v[i][0]; j--)
{
//只主件
dp[j]=max(dp[j],dp[j-v[i][0]]+w[i][0]);
//主件+第一个附件
if(j-v[i][0]-v[i][1]>=0)
{
dp[j]=max(dp[j],dp[j-v[i][0]-v[i][1]]+w[i][0]+w[i][1]);
}
//主件+第二个附件
if(j-v[i][0]-v[i][2]>=0)
{
dp[j]=max(dp[j],dp[j-v[i][0]-v[i][2]]+w[i][0]+w[i][2]);
}
//主件+第一个附件+第二个附件
if(j-v[i][0]-v[i][1]-v[i][2]>=0)
{
dp[j]=max(dp[j],dp[j-v[i][0]-v[i][1]-v[i][2]]+w[i][0]+w[i][1]+w[i][2]);
}
}
}
printf("%d",dp[m]);
return 0;
}