背包问题
0-1背包
这是最简单的背包问题,简而言之就是每件物品只有一样,可以取或者不取,对于容量为V的背包,N件物品,每一件都可以尝试放入背包中,那么顺序就是遍历这N件物品,每遍历到一个新的物品,都尝试将当前物品放入背包中,看看是放入后得到的价值高还是不放的价值高,取最高者即可。
所以需要一个二维数组w[N][V],这个二维数组中的每一个位置w[i][j]代表的意思是前i个物品在容量为j的时候可以获取的最大价值是多少。那么选当前物品(重量W,价值Va)得到的价值就是w[i - 1][j - W] + Va,不选当前物品的价值为w[i - 1][j],选最大值就行。
状态转移方程为:
题目:编号a,b,c,d,e的5件物品,重量分别是2,2,6,5,4,价值分别是6,3,5,4,6,现在背包承重为10,如何使装入背包的物品具有最大值?
如果采用状态转移方程,我们可以知道二维数组的值为:
0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 |
---|---|---|---|---|---|---|---|---|---|---|
a | 0 | 0 | 6 | 6 | 6 | 6 | 6 | 6 | 6 | 6 |
b | 0 | 0 | 6 | 6 | 9 | 9 | 9 | 9 | 9 | 9 |
c | 0 | 0 | 6 | 6 | 9 | 9 | 9 | 9 | 10 | 13 |
d | 0 | 0 | 6 | 6 | 9 | 9 | 9 | 10 | 11 | 13 |
e | 0 | 0 | 6 | 6 | 9 | 9 | 12 | 12 | 15 | 15 |
代码如下:
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
//二维数组求0-1背包
int main()
{
int n, m; //n:物品个数,m:背包容量
while (cin >> n >> m) {
vector<pair<int, int>> vw(n + 1); //价值+重量
for (int i = 1; i <= n; ++i)
cin >> vw[i].first >> vw[i].second;
vector<vector<int>> ret(n + 1, vector<int>(m + 1, 0)); //计算数组,选择或者不选择
for (int i = 1; i <= n; ++i) {
for (int j = 1; j <= m; ++j) {
if (vw[i].second > j) { //背包容量还不够装当前的东西
ret[i][j] = ret[i - 1][j]; //那就只能放可以放的(就是上一行,不包括当前物品的情况)
}
else { //可以装当前物品,就看看在没有当前物品的前提下(就是参考上一行),装和不装哪个更好
ret[i][j] = max(ret[i - 1][j], ret[i - 1][j - vw[i].second] + vw[i].first);
}
}
}
cout << ret[n][m] << endl;
}
return 0;
}
这里可以优化到一维数组,这样可以减少空间复杂度,具体方法是将第二层循环颠倒,从容量为满到空进行反向计算,这样每次循环的时候,面对的背包都是不包含当前物品的背包,使用数值进行计算即可。
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
int main()
{
int n, m; //n:物品个数,m:背包容量
while (cin >> n >> m) {
vector<pair<int, int>> vw(n + 1); //价值+重量
for (int i = 1; i <= n; ++i)
cin >> vw[i].first >> vw[i].second;
vector<int> ret(m + 1, 0); //一维数组,反向求0-1背包
for (int i = 1; i <= n; ++i) {
for (int j = m; j >= 1; --j) {
if(vw[i].second <= j)
ret[j] = max(ret[j], ret[j - vw[i].second] + vw[i].first); //去掉当前物品的重量+当前物品的价值和原价值相比
}
}
cout << ret[m] << endl;
}
return 0;
}
完全背包
完全背包问题和0-1背包问题不同的地方在于,完全背包的每件物品都是无穷多件,这样我们就不用像0-1背包一样,每次都选择不包括当前物品的背包来尝试添加当前物品(因为每件物品只能添加一次),而是可以在包括当前物品的背包中,选择继续添加当前物品。
所以需要一个一维数组w[N],这个数组中的每一个位置w[i]代表的意思是在容量为j的时候可以获取的最大价值是多少。那么添加当前物品(第一次或是多次)(重量W,价值Va)得到的价值就是w[i - W] + Va,不添加当前物品的价值为w[i],选最大值就行。
状态转移方程为:
那么可以用一维数组来存储背包的容量产生的最大价值,设数组为f[m],那么f[i]代表i容量的背包可以产生的最大价值。
牛客网-牛牛炒股票
题目:牛牛得知了一些股票今天买入的