文章目录
1. 01 01 01背包
题目简介:
有 n n n个物品和一个容量为 v v v的背包,每个物品的价值为 c [ i ] c[i] c[i],体积为 w [ i ] w[i] w[i],要求选择一些物品放入背包中,使物品总体积不超过 m m m的前提下,物品的总价值最大,求最大总价值。
基本思路:
这是最基础的背包问题,特点是:每种物品仅有一件,可以选择放或不放。用子问题定义状态:即 f [ i ] [ j ] f[ i ][ j ] f[i][j]表示前 i i i 件物品恰放入一个容量为 j j j的背包可以获得的最大价值。则其状态转移方程便是:
f[i][j]=max(f[i-1][j],f[i-1][j-w[i]]+c[i]);
/*
f[i-1][v]为不取第i件物品,让前i-1件物品来装v这么大的空间
f[i-1][v-w[i]]+c[i]为取第i件物品,让前i-1件物品来装v-w[i]这
么大的空间(因为要给第i件物品腾空间,所以是v-w[i]),然后加上
第i件物品的价值。
这里要注意,j要大于等于w[i]
*/
核心代码如下:
for(int i=1;i<=n;i++) {
for(int j=0;j<=m;j++) {
f[i][j]=f[i-1][j];
if(f[i-1][j-w[i]]+c[i]>f[i][j])
f[i][j]=f[i-1][j-w[i]]+c[i];
}
}
优化:
细细一品,其实我们可以用滚动数组来优化,因为我们可以发现第 i i i个阶段的决策只与第 i − 1 i - 1 i−1阶段有关,所以我们第一维只用开 2 2 2;
核心代码:
int f[2][1005];
//第二维的大小就是v的范围,我这里是随便编得一个
for(int i=1;i<=n;i++) {
for(int j=0;j<=m;j++) {
f[i&1][j]=f[(i-1)&1][j];
if(f[(i-1)&1][j-w[i]]+c[i]>f[i&1][j])
f[i&1][j]=f[(i-1)&1][j-w[i]]+c[i];
}
}
再细细一品,在每个阶段的开始时,实际上执行了一次从 f [ i − 1 ] [ ] f[ i-1 ][] f[i−1][]到 f [ i ] [ ] f[ i ][] f[i][]的拷贝操作,于是我们就可以将二维改为一维。 f [ j ] f[ j ] f[j]表示背包中放入总体积为 j j j 的物品的总价值。
核心代码:
for(int i=1;i<=n;i++) {
for(int j=m;j>=w[i];j--) {
f[j]=max(f[j],f[j-w[i]]+c[i]);
}
}
注意,这里我们是用的倒序循环。循环到 j j j时:
f f f数组中 f [ j m ] f[j~m] f[j m]处于第 i i i个阶段, f [ 0 j