动态规划
01背包
题目描述
现有编号分别为1 2 3 4 5的五件物品,它们的重量分别是2 2 6 5 4,价值分别是6 3 5 6 4,现在给你个承重为10的背包,如何让背包里装入的物品具有最大的价值总和?
物品重量w[] = {2, 2, 6, 5, 4}; 物品价值v[] = {6, 3, 5, 6, 4}; 背包容量为c;
假设opt[i][j]表示前i件物品放入容量为j的背包中可获得的最大价值,则对于第i件物品来说,有两种情况:
(1) 当w[i] > c时,无法放入背包,此时opt[i][j] = opt[i-1][j];
(2) 当w[i] <= c时,对于第i件物品来说有两种选择:放入背包或不放;
a. 不放:opt[i][j] = opt[i-1][j];前i件物品可获得的最大价值即是前(i-1)件物品放入容量为j的背包中获得的最大价值;
b. 放入:opt[i][j] = opt[i-1][j-w[i]]+v[i];前i件物品可获得的最大价值即是前(i-1)件物品放入容量为(j - w[i])的背包中获得的最大价值加上第i件物品的价值。
综上,01背包的状态转换方程如下:
代码如下:
#include<iostream>
#include<vector>
#include<algorithm>
using namespace std;
int main()
{
int n; // 物品数量
int c; // 背包容量
cin >> n >> c;
vector<int> v(n + 1, 0); // 物品价值,首元素无意义,从v[1]开始
vector<int> w(n + 1, 0); // 物品重量,首元素无意义,从w[1]开始
for (int i = 1; i <= n; i++)
cin >> v[i];
for (int i = 1; i <= n; i++)
cin >> w[i];
vector<vector<int>> opt(n + 1, vector<int>(c + 1, 0));
for (int i = 1; i <= n; i++)
{
for (int j = 1; j <= c; j++)
{
if (w[i] > j)
opt[i][j] = opt[i - 1][j];
else
opt[i][j] = max(opt[i - 1][j], opt[i - 1][j - w[i]] + v[i]);
}
}
cout << opt[n][c] << endl;
return 0;
}
上述方法的时间复杂度和空间复杂度均为O(nc),其中空间复杂度是可以优化到O(c)的。
由状态转换方程和状态表可知,第i次循环得到的最大价值总和是由第(i-1)次循环递推而来的;即:opt[i][j]是由opt[i-1][j]和opt[i-1][j-w[i]]+v[i]两个子问题递推得到的。要想得到opt[j] (背包容量为j时可获得的最大价值总和),则第二个for循环语句中我们需要让j从容量c递减至1的递减顺序来计算opt[j],这样才能保证计算opt[j]时opt[j-w[i]]保存的是状态opt[i-1][j-w[i]]的值。(若还是让j从1->n递增,那么前面的opt[j]会覆盖计算后面的opt[j]值时前一次的状态值)。
综上,状态转换方程如下:
代码如下:
#include<iostream>
#include<vector>
#include<algorithm>
using namespace std;
int main()
{
int n, c;
cin >> n >> c;
vector<int> v(n + 1, 0);
vector<int> w(n + 1, 0);
for (int i = 1; i <= n; i++)
cin >> v[i];
for (int j = 1; j <= n; j++)
cin >> w[j];
vector<int> opt(c + 1, 0);
for (int i = 1; i <= n; i++)
{
for (int j = c; j >= 1; j--)
{
if (w[i] <= j)
opt[j] = max(opt[j], opt[j - w[i]] + v[i]);
}
}
cout << opt[c] << endl;
return 0;
}