背包问题
给定一组物品,每种物品都有自己的重量和价值,现有一个背包,能承受的重量有限,在受限制的重量下,取若干物品,使得总价值最大。这一类问题,被称为背包问题。
01背包(物品个数为1)
for (int i = 1; i <= N; ++i) { for (int j = 0; j <= V; ++j) { if(j >= c[i]) { dp[i][j] = max(dp[i - 1][j - c[i]] + w[i], dp[i - 1][j]); } else { dp[i][j] = dp[i - 1][j]; } } }
时间上是两重循环,时间复杂度为O(NV)。空间是二维的,空间复杂度也为O(NV)。
for (int i = 1; i <= n; ++i) for (int j = v; j >= c[i]; --j) { dp[j] = max(dp[j - c[i]] + w[i], dp[j]); }
这个做法空间复杂度也为O(V)。
多重背包(物品个数有限)
for (int i = 1; i <= N; i++) { for (int j = 0; j <= V; j++) { for (int k = 0; k <= n[i]; k++) { if (j >= c[i] * k) { dp[i][j] = max(dp[i - 1][j - c[i] * k] + w[i] * k, dp[i][j]); } } } }
这一份代码和01背包相比,不再有else部分了,因为,k = 0的时候dp[i][j] = max(dp[i - 1][j], dp[i][j]), 相当于01背包的else部分。
空间优化
既然多重背包的可以转换成01背包,那么我们必然也可以像01背包那样优化空间复杂度。还是按照从大到小的顺序枚举背包体积。
for (int i = 1; i <= N; i++) { for (int j = V; j >= 0; j--) { for (int k = 1; k <= n[i]; k++) { if (j >= c[i] * k) { dp[j] = max(dp[j - c[i] * k] + w[i] * k, dp[j]); } } } }
完全背包(物品个数无限)
解析
虽然物品个数是无限的,但是实际上,由于背包容量有上限,每个物品最多选取的个数也是有限制的,这样可以转换成多重背包问题,进而可以转换成01背包问题。
可以用多重背包的思想来解决完全背包。
for (int i = 1; i <= N; i++) { for (int j = 0; j <= V; j++) { for (int k = 0; k * c[i] <= j; k++) { dp[i][j] = max(dp[i - 1][j - c[i] * k] + w[i] * k, dp[i][j]); } } }
for (int i = 1; i <= n; i++) { for (int j = 0; j <= v; j++) { if (j >= c[i]) { dp[i][j] = max(dp[i][j - c[i]] + w[i], dp[i - 1][j]); } else { dp[i][j] = dp[i - 1][j]; } } }
这样我们的的算法时间复杂度O(NV),空间复杂度O(NV)。
不难发现,我们也可以把空间复杂度优化下来,优化成O(V)。
for (int i = 1; i <= n; i++) { for (int j = c[i]; j <= v; j++) { dp[j] = max(dp[j - c[i]] + w[i], dp[j]); } }
与 01 背包相比,完全背包只是第二重循环的顺序发生了翻转。
多重背包的二进制优化
1 #include <iostream> 2 using namespace std; 3 int n[110], c[110], w[110]; 4 int nc[1000], nw[1000]; 5 int dp[5010]; 6 int main() { 7 int N, V; 8 cin >> N >> V; 9 for (int i = 1; i <= N; ++i) { 10 cin >> w[i] >> c[i] >> n[i]; 11 } 12 int ncnt = 0; 13 // 二进制拆分 14 for (int i = 1; i <= N; ++i) { 15 int k; 16 // 找到最大的 k 17 for (k = 1; n[i] - (1 << k) + 1 > 0; ++k) { 18 nc[ncnt] = (1 << (k - 1)) * c[i]; 19 nw[ncnt] = (1 << (k - 1)) * w[i]; 20 ++ncnt; 21 } 22 --k; 23 // 最后一组 24 nc[ncnt] = (n[i] - (1 << k) + 1) * c[i]; 25 nw[ncnt] = (n[i] - (1 << k) + 1) * w[i]; 26 ++ncnt; 27 } 28 // 01 背包 29 for (int i = 0; i < ncnt; ++i) { 30 for (int j = V; j >= nc[i]; --j) { 31 dp[j] = max(dp[j], dp[j - nc[i]] + nw[i]); 32 } 33 } 34 cout << dp[V] << endl; 35 return 0; 36 }
-
来源:https://www.cnblogs.com/jiamian/p/12209162.html