给出背包容量$V$,$n$件物品,每件物品的体积为$v_i$, 价值为$w_i$,每件物品只能选1次或不选,选取物品总体积不超过$V$的情况下,最大总价值是多少?
定义$f[i,v]$为前$i$件物品放入一个容量为$v$的背包可以获得的最大价值。$f[N,V]$为所求。
$$ f[i,v]=max\lbrace f[i-1, v],f[i-1, v-c_i]+w_i\rbrace $$
// [<https://www.acwing.com/problem/content/2/>](<https://www.acwing.com/problem/content/2/>)
// WA,j<vi时没有转移
#include <bits/stdc++.h>
using namespace std;
int main() {
int n, v;
cin >> n >> v;
vector<vector<int>> f(n+1, vector<int>(v+1, 0));
for (int i=1; i<=n; i++) {
int vi, wi;
cin >> vi >> wi;
for (int j=vi; j<=v; j++) {
f[i][j] = max(f[i-1][j], f[i-1][j-vi]+wi);
}
}
cout << f[n][v] << "\\n";
return 0;
}
// AC
#include <bits/stdc++.h>
using namespace std;
int main() {
int n, v;
cin >> n >> v;
vector<vector<int>> f(n+1, vector<int>(v+1, 0));
for (int i=1; i<=n; i++) {
int vi, wi;
cin >> vi >> wi;
for (int j=0; j<=v; j++) {
if (j>=vi) f[i][j] = max(f[i-1][j], f[i-1][j-vi]+wi);
else f[i][j] = f[i-1][j];
}
}
cout << f[n][v] << "\\n";
return 0;
}
// AC
#include <bits/stdc++.h>
using namespace std;
int main() {
int n, v;
cin >> n >> v;
vector<vector<int>> f(n+1, vector<int>(v+1, 0));
for (int i=1; i<=n; i++) {
int vi, wi;
cin >> vi >> wi;
for (int j=0; j<vi; j++) {
f[i][j] = f[i-1][j];
}
for (int j=vi; j<=v; j++) {
f[i][j] = max(f[i-1][j], f[i-1][j-vi]+wi);
}
}
cout << f[n][v] << "\\n";
return 0;
}
优化空间复杂度,使用滚动数组,自动将f[i-1][] → f[i][]
// [<https://www.acwing.com/problem/content/2/>](<https://www.acwing.com/problem/content/2/>)
#include <bits/stdc++.h>
using namespace std;
int main() {
int n, v;
cin >> n >> v;
vector<int> f(v+1);
for (int i=1; i<=n; i++) {
int vi, wi;
cin >> vi >> wi;
for (int j=v; j>=vi; j--) {
f[j] = max(f[j], f[j-vi]+wi);
}
}
cout << f[v] << "\\n";
return 0;
}
初始化的问题,有的题目要求恰好装满背包,有的则不需要背包装满。
要求恰好装满背包,那么在初始化时除了 $f[0]$ 为 0,其它 $f[1...V]$ 均设为负无穷,这样就可以保证最终得到的$f[V]$是一种恰好装满背包的最优解。
如果并没有要求必须把背包装满,而是只希望价格尽量大,初始化时应该将$f[0…v]$全部设为 0。
给出背包容量$V$,$N$件物品,每件物品的体积为$v_i$, 价值为$w_i$,每件物品能选无数次或不选,选取物品总体积不超过$V$的情况下,最大总价值是多少?
定义$f[i,v]$为前$i$件物品放入一个容量为$v$的背包可以获得的最大价值。$f[N,V]$为所求。
$$ f[i,v]=max\lbrace f[i-1, v-k\cdot c_i]+k\cdot w_i|0\le k\cdot c_i \le v\rbrace $$
状态$f[i,v]$的求解时间是$O(\frac{v}{c_i})$,总求解时间复杂度为$O(NV\sum \frac{V}{c_i})$
// <https://www.acwing.com/problem/content/3/>
// TLE
#include <bits/stdc++.h>
using namespace std;
int main() {
int n, v;
cin >> n >> v;
vector<vector<int>> f(n+1, vector<int>(v+1, 0));
for (int i=1; i<=n; i++) {
int vi, wi;
cin >> vi >> wi;
// for (int j=0; j<=v; j++) {
// f[i][j] = f[i-1][j];
// }
for (int j=0; j<=v; j++) {
for (int k=0; k*vi<=j; k++) {
f[i][j] = max(f[i][j], f[i-1][j-k*vi]+k*wi);
}
}
}
cout << f[n][v] << "\\n";
return 0;
}
优化
$\begin{array}{lll} f[i,v]& = & max\lbrace f[i-1, v-k\cdot c_i]+k\cdot w_i|0\le k\cdot c_i \le v\rbrace \\\\ & = & max\lbrace f[i-1, v], f[i-1, v- c_i]+w_i, \cdots, f[i-1, v-\lfloor \frac{v}{c_i} \rfloor \cdot c_i]+\lfloor \frac{v}{c_i} \rfloor\cdot w_i\rbrace \end{array}$
$\begin{array}{lll} f[i,v-c_i]& = & max\lbrace f[i-1, v-c_i-k\cdot c_i]+k\cdot w_i|0\le k\cdot c_i \le v-c_i\rbrace \\\\ & = & max\lbrace f[i-1, v-c_i], f[i-1, v- 2c_i]+w_i, \cdots, f[i-1, v-c_i-(\lfloor \frac{v}{c_i} \rfloor-1) \cdot c_i]+(\lfloor \frac{v}{c_i} \rfloor-1)\cdot w_i\rbrace \end{array}$