背包问题
背包问题本质上是求取全组合问题,可做的选择在外循环,遍历的范围在内循环
一. 0-1背包问题
有N件物品和一个容量为V的背包。第i件物品的费用是c[i],价值是w[i]。求解将哪些物品装入背包可使价值总和最大。
该类型题目的特点是:每种物品仅有一件,可以选择放或不放。直接回溯搜索的时间复杂度非常大,必须使用动态规划求解
dp[i][j]表示前i件物品恰放入一个容量为j的背包可以获得的最大价值
从而可以实现状态的转移,并保证无后效性
for(int i=1;i<=N;i++)//遍历可选物品,每个只能选一次
for(int j=V;j>=0;j--)//从后往前遍历可容纳空间
dp[i][j]=max{dp[i-1][v],dp[i-1][j-c[i]]+w[i]}
空间一维优化
for(int i=1;i<=N;i++)//遍历可选物品,每个只能选一次
for(int j=V;j>=0;j--)//遍历可容纳空间
dp[j]=max{dp[v],dp[j-c[i]]+w[i]} //空间一维优化
C++模板
int maxval(int V,vector<int>&w,vector<int>&c){
int n = w.size();
vector<int> dp(V+1);
int res = 0;
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]);
res = max(res,dp[j]);
}
return res;
}
1.1. 采药
1.2. 装箱问题
1.3. 小A点菜
1.4. 精卫填海
1.5. 5倍经验日
1.6. 最大约数和
1.7. 考前临时抱佛脚
1.8. Subset Sum
1.9. U盘
1.10. 书架
二. 完全背包问题
有N种物品和一个容量为V的背包,每种物品都有无限件可用。第i种物品的费用是c[i],价值是w[i]。
for(int i=1;i<=N;i++)//遍历可选物品,每个只能选一次
for(int j=0;j<=V;j++)//从前往后遍历可容纳空间,遍历过程中,可以将同一物品选择多次
dp[i][j]=max{dp[i-1][j],dp[i-1][j-c[i]]+w[i]}
C++模板
int maxval(int v,vector<int>&c,vector<int>&w){
int n = w.size();
vector<int> dp(v+1);
int res = 0;
for(int i=0;i<n;i++)
for(int j=c[i];j<=v;j++){
dp[j] = max(dp[j],dp[j-c[i]]+w[i]);
res = max(res,dp[j]);
}
return res;
}
2.1. 疯狂的采药
三. 多重背包问题
有N种物品和一个容量为V的背包。第i件物品的费用是c[i],价值是w[i]。第i种物品最多有n[i]件可用
其实将第i件物品拆成n[i]件物品,就转化成了0-1背包问题
如果n[i]值过大的话会使得时间复杂度过大,这里可以使用二进制技巧,将拆分的物品价值和费用皆转化为1,2,4,8
从而保证可以凑出0~n[i]中的任意数值,同时使得时间复杂度为对数级别
3.1. 砝码称重
四.混合背包问题
有的物品只可以取一次(01背包),有的物品可以取无限次(完全背包),有的物品可以取的次数有一个上限(多重背包)
for i=1..N
if 第i件物品属于01背包
ZeroOnePack(c[i],w[i])
else if 第i件物品属于完全背包
CompletePack(c[i],w[i])
else if 第i件物品属于多重背包
MultiplePack(c[i],w[i],n[i])
//0-1背包
procedure ZeroOnePack(cost,weight)
for v=V..cost
f[v]=max{f[v],f[v-cost]+weight}
//完全背包
procedure CompletePack(cost,weight)
for v=cost..V
f[v]=max{f[v],f[v-c[i]]+w[i]}
//多重背包
procedure MultiplePack(cost,weight,amount)
if cost*amount>=V //对应数目的容量超过上限,相当于无限多
CompletePack(cost,weight)
return
integer k=1
while k<amount //否则转化为多个0-1背包问题,二进制拆解
ZeroOnePack(k*cost,k*weight)
amount=amount-k
k=k*2
ZeroOnePack(amount*cost,amount*weight)
4.1. 樱花
五. 二维背包问题
对于每件物品,具有两种不同的费用;选择这件物品必须同时付出这两种代价,通常另一种费用是个数限制
f[i][v][u]=max{f[i-1][v][u],f[i-1][v-a[i]][u-b[i]]+w[i]}
C++模板
int maxval(int v,int m,vector<int>&weight,vector<int>&volume,vector<int>&w){
int n = w.size();
int dp[v+1][m+1];
memset(dp,0,sizeof(dp));
int res = 0;
for(int i=0;i<n;i++)
for(int j=v;j>=volume[i];j--)
for(int k=m;k>=weight[i];k--){
dp[j][k] = max(dp[j][k],dp[j-volume[i]][k-weight[i]]+w[i]);
res = max(res,dp[j][k]);
}
return res;
}
5.1. NASA的食物计划
六. 分组背包问题
N件物品和一个容量为V的背包。第i件物品的费用是c[i],价值是w[i]。这些物品被划分为若干组,每组中的物品互相冲突,最多选一件
相比之前的问题,做选择不再是单个物品,而是分组
for 所有的组k
for v=V..0
for 所有的i属于组k
f[v]=max{f[v],f[v-c[i]]+w[i]}