背包的基本模型:
给你一个容量为V的背包和若干种物品,在一定的限制条件下(每种物品都占用一定容量),问最多能放进多少价值的物品?
背包是很典型、最基本的DP问题
背包的每个容量都是“状态”
背包问题的分类
01背包
完全背包 多重背包
二维费用背包 混合三种背包 分组背包 有依赖的背包
一、01背包
问题:
有 N 件物品和一个容量为 V 的背包。第 i 件物品的费用是 c[i] ,价值是w[i] 。求解将哪些物品装入背包可使这些物品的费用总和不超过背包容量,且价值总和最大。
问题分析:
这是最基础的背包问题,特点是:每种物品仅有一件,可以选择放或不放。
用子问题定义状态:即dp[i][v]表示前i件物品恰放入一个容量为v的背包可以获得的最大价值。
则其状态转移方程便是:dp[i][v]=max{dp[i-1][v],dp[i-1][v-c[i]]+w[i]}
代码模板:
#include<bits/stdc++.h>
using namespace std;
int dp[1001],w[1001],c[1001];
int main(){
int n,v;
cin>>n>>v;
for(int i=0;i<n;i++) cin>>c[i]>>w[i];
memset(dp,0,sizeof(dp));
for(int i=0;i<n;i++)
for(int j=v;j>=c[i];j--)
dp[j]=max(dp[j],dp[j-c[i]]+w[i]);
cout<<dp[v]<<endl;
return 0;
}
二、完全背包:
问题:
有 N 种物品和一个容量为 V 的背包,每种物品都有无限件可用。第i种物品的费用是C[i] ,价值是W[i] 。求解将哪些物品装入背包可使这些物品的费用总和不超过背包容量,且价值总和最大。
问题分析:
特点,背包可以取无数次。
伪代码:
for(int i=0;i<n;i++)
for(int v=0;i<v;i++)
f[v]=max{f[v],f[v-c[i]]+w[i]};
将这一段代码替换01背包的问题代码即可实现。这正是为了保证每件物品只选一次,保证在考虑“选入第 i 件物品”这件策略时,依据的是一个绝无已经选入第 i 件物品的子结果f([i-1][v-c[i]]) 。而现在完全背包的特点恰是每种物品可选无限件,所以在考虑“加选一件第i种物品”这种策略时,却正需要一个可能已选入第i种物品的子结果 f([i][v-c[i]]),所以就可以并且必须采用 0-V 的顺序循环。
三、多重背包:
问题:
有 N 种物品和一个容量为 V 的背包。第i种物品最多有 n[i]件可用,每件费用是 c[i] ,价值是 w[i] 。求解将哪些物品装入背包可使这些物品的费用总和不超过背包容量,且价值总和最大。
问题分析:
这题目和完全背包问题很类似。基本的方程只需将完全背包问题的方程略微一改即可,因为对于第i种物品有 n[ i ] +1 种策略:取 0 件,取 1 件……取 n[ i ] 件。令 f[ i ][ v ] 表示前 i 种物品恰放入一个容量为 v 的背包的最大权值.
四、混合三种背包问题:
问题:
如果将前三种混合起来,也就是说,有的物品只可以取一次(01背包),有的物品可以取无限次(完全背包),有的物品可以取的次数有一个上限(多重背包)。应该怎么求解呢?
问题分析:
此问题就是前三个背包的混合问题,可以先看成01背包和完全背包问题,只取一个或取无穷个,那么只需在对每个物品应用转移方程时,根据物品的类别选用顺序或逆序的循环即可。
if是01背包则{
for(int i=0;i<n;i++)
for(int j=v;j>=c[i];j--)
dp[j]=max(dp[j],dp[j-c[i]]+w[i]);
}
else {
for(int i=0;i<n;i++)
for(int v=0;i<v;i++)
f[v]=max{f[v],f[v-c[i]]+w[i]};
}
五、二维费用的背包问题
问题:
二维费用的背包问题是指:对于每件物品,具有两种不同的费用;选择这件物品必须同时付出这两种代价;对于每种代价都有一个可付出的最大值(背包容量)。问怎样选择物品可以得到最大的价值。设这两种代价分别为代价 1 和代价 2 ,第i件物品所需的两种代价分别为 a[ i ] 和 b [ i ] 。两种代价可付出的最大值(两种背包容量)分别为 V 和 U 。物品的价值为 w[ i ] 。
问题分析:
费用加了一维,只需状态也加一维即可。设f[ i ][ v ][ u ]表示前 i 件物品付出两种代价分别为 v 和 u 时可获得的最大价值。状态转移方程就是:
f[ i ][ v ][ u ]= max(f[ i-1 ][ v ][ u ],f[ i-1 ][ v-a[ i ] ][ u - b[ i ]]+ w[ i ])
如前述方法,可以只使用二维的数组:当每件物品只可以取一次时变量 v 和 u 采用顺序的循环,当物品有如完全背包问题时采用逆序的循环。当物品有如多重背包问题时拆分物品。