0-1背包问题
每件物品有且仅有一件。
动态规划的关键是建立一个动态规划表,上表图6.17部分有错误,下表为改正后的表。表的建立从左上角按行计算,直到右下角。行j代表可供选择的物品,2行表示:0,1,2号物品可供选择。列i代表背包空间。表中的每一个值表示,若背包有i剩余空间,在0-j号物品中选择,能获得的最大价值。
若放当前物品:总价值为当前物品价值+剩余空间能放的最大价值;若不放当前物品:则总价值为当前空间能放的最大价值(上一行的值)。
vector<bool> knasSack(int n, int c,int *weight,int *value)
{
vector<vector <int>> maxValue(n + 1, vector<int>(c + 1, 0));
vector<bool> result(n, false);
for (int i = 1; i <= n; i++)
{
for (int j = 1; j <= c; j++)
{
if (i == 1)
{
maxValue[i][j] = (weight[i-1] <= j ? value[i-1] : 0);
}
else
{
int topValue = maxValue[i - 1][j]; // 上一个网格的值
//int thisValue = (weight[i-1] <= j ? // 当前商品的价值 + 剩余空间的价值
// (j - weight[i-1] > 0 ? value[i-1] + maxValue[i - 1][j - weight[i-1]] : value[i-1])
// : topValue);
int thisValue = weight[i - 1] <= j ? value[i - 1] + maxValue[i - 1][j - weight[i - 1]] : topValue;
// 返回 topValue和thisValue中较大的一个
maxValue[i][j] = (topValue > thisValue ? topValue : thisValue);
}
}
}
printVec2i(maxValue);
for (int i = n, j = c; i > 0; i--) {
if (maxValue[i][j] > maxValue[i - 1][j])
{
result[i - 1] = true;
j = j - weight[i-1];
}
}
return result;
}
0-1背包扩展:背包必须装满
在初始化时不能初始化为0,而要初始化为错误状态。在赋值时也加入对错误状态的判断。
vector<bool> knasSack_full(int n, int c, int *weight, int *value)
{
vector<vector <int>> maxValue(n + 1, vector<int>(c + 1, -1));
vector<bool> result(n, false);
for (int i = 1; i <= n; i++)
{
for (int j = 1; j <= c; j++)
{
if (i == 1)
{
maxValue[i][j] = (weight[i - 1] == j ? value[i - 1] : -1);
}
else
{
int topValue = maxValue[i - 1][j]; // 上一个网格的值
int thisValue = -1;
if (weight[i - 1] == j) thisValue = value[i - 1];
if (weight[i - 1] < j)
{
if (maxValue[i - 1][j - weight[i - 1]] == -1) thisValue = -1;
else thisValue = value[i - 1] + maxValue[i - 1][j - weight[i - 1]];
}
// 返回 topValue和thisValue中较大的一个
maxValue[i][j] = (topValue > thisValue ? topValue : thisValue);
}
}
}
printVec2i(maxValue);
for (int i = n, j = c; i > 0; i--) {
if (maxValue[i][j] > maxValue[i - 1][j])
{
result[i - 1] = true;
j = j - weight[i - 1];
}
}
return result;
}
多重背包问题
物品有指定件,只需用num数组对weight和value进行重新初始化即可。
int knasSack_kTimes(int n, int c, int *weight, int *value, int *num)
{
vector<int> weightVec;
vector<int> valueVec;
for (int i = 0; i < n; i++)
{
while (num[i]--)
{
weightVec.push_back(weight[i]);
valueVec.push_back(value[i]);
}
}
/*
.......
.......
.......
*/
return 0;
}
完全背包问题
物品件数有无穷多,初始化时从右向左初始化即可。加入变量times表示放了几件当前物品。
int knasSack_nTimes(int n, int c, int *weight, int *value)
{
vector<vector <int>> maxValue(n + 1, vector<int>(c + 1, 0));
vector<bool> result(n, false);
for (int i = 1; i <= n; i++)
{
for (int j = c; j >= 1; j--)
{
if (i == 1)
{
maxValue[i][j] = (j / weight[i - 1] > 0 ? value[i - 1] * (int)(j / weight[i - 1]) : 0);
}
else
{
int topValue = maxValue[i - 1][j]; // 上一个网格的值
int times = j / weight[i - 1];
int thisValue = 0;
if (times > 0)
{
for (int k = 1; k <= times; k++)
{
thisValue = value[i - 1] * k + maxValue[i - 1][j - weight[i - 1] * k] > thisValue ? value[i - 1] * k + maxValue[i - 1][j - weight[i - 1] * k] : thisValue;
}
}
// 返回 topValue和thisValue中较大的一个
maxValue[i][j] = (topValue > thisValue ? topValue : thisValue);
}
}
}
printVec2i(maxValue);
return maxValue[n][c];
}
运行结果
分别为0-1背包问题,0-1背包问题扩展,完全背包问题的运行结果
完整代码
#include <iostream>
#include <vector>
#include "a_arrayAndMatrix.h"
using namespace std;
vector<bool> knasSack(int n, int c, int *wight, int *value);
vector<bool> knasSack_full(int n, int c, int *weight, int *value);
int knasSack_kTimes(int n, int c, int *weight, int *value, int *num);
int knasSack_nTimes(int n, int c, int *weight, int *value);
int main()
{
int *weight = new int[5]{ 2,2,6,5,4 };
int *value = new int[5]{ 6,3,5,4,6 };
int n = 5;
int c = 10;
int maxVal = 0;
vector<bool> result = knasSack(n, c, weight, value);
for (int i = 0; i < result.size(); i++)
{
if (result[i])
{
maxVal += value[i];
}
}
cout << maxVal << endl;
maxVal = 0;
vector<bool> result2 = knasSack_full(n, c, weight, value);
for (int i = 0; i < result2.size(); i++)
{
if (result2[i])
{
maxVal += value[i];
}
}
cout << maxVal << endl;
cout << knasSack_nTimes(n, c, weight, value) << endl;
system("pause");
return 0;
}
vector<bool> knasSack(int n, int c,int *weight,int *value)
{
vector<vector <int>> maxValue(n + 1, vector<int>(c + 1, 0));
vector<bool> result(n, false);
for (int i = 1; i <= n; i++)
{
for (int j = 1; j <= c; j++)
{
if (i == 1)
{
maxValue[i][j] = (weight[i-1] <= j ? value[i-1] : 0);
}
else
{
int topValue = maxValue[i - 1][j]; // 上一个网格的值
//int thisValue = (weight[i-1] <= j ? // 当前商品的价值 + 剩余空间的价值
// (j - weight[i-1] > 0 ? value[i-1] + maxValue[i - 1][j - weight[i-1]] : value[i-1])
// : topValue);
int thisValue = weight[i - 1] <= j ? value[i - 1] + maxValue[i - 1][j - weight[i - 1]] : topValue;
// 返回 topValue和thisValue中较大的一个
maxValue[i][j] = (topValue > thisValue ? topValue : thisValue);
}
}
}
printVec2i(maxValue);
for (int i = n, j = c; i > 0; i--) {
if (maxValue[i][j] > maxValue[i - 1][j])
{
result[i - 1] = true;
j = j - weight[i-1];
}
}
return result;
}
vector<bool> knasSack_full(int n, int c, int *weight, int *value)
{
vector<vector <int>> maxValue(n + 1, vector<int>(c + 1, -1));
vector<bool> result(n, false);
for (int i = 1; i <= n; i++)
{
for (int j = 1; j <= c; j++)
{
if (i == 1)
{
maxValue[i][j] = (weight[i - 1] == j ? value[i - 1] : -1);
}
else
{
int topValue = maxValue[i - 1][j]; // 上一个网格的值
int thisValue = -1;
if (weight[i - 1] == j) thisValue = value[i - 1];
if (weight[i - 1] < j)
{
if (maxValue[i - 1][j - weight[i - 1]] == -1) thisValue = -1;
else thisValue = value[i - 1] + maxValue[i - 1][j - weight[i - 1]];
}
// 返回 topValue和thisValue中较大的一个
maxValue[i][j] = (topValue > thisValue ? topValue : thisValue);
}
}
}
printVec2i(maxValue);
for (int i = n, j = c; i > 0; i--) {
if (maxValue[i][j] > maxValue[i - 1][j])
{
result[i - 1] = true;
j = j - weight[i - 1];
}
}
return result;
}
int knasSack_kTimes(int n, int c, int *weight, int *value, int *num)
{
vector<int> weightVec;
vector<int> valueVec;
for (int i = 0; i < n; i++)
{
while (num[i]--)
{
weightVec.push_back(weight[i]);
valueVec.push_back(value[i]);
}
}
/*
.......
.......
.......
*/
return 0;
}
int knasSack_nTimes(int n, int c, int *weight, int *value)
{
vector<vector <int>> maxValue(n + 1, vector<int>(c + 1, 0));
vector<bool> result(n, false);
for (int i = 1; i <= n; i++)
{
for (int j = c; j >= 1; j--)
{
if (i == 1)
{
maxValue[i][j] = (j / weight[i - 1] > 0 ? value[i - 1] * (int)(j / weight[i - 1]) : 0);
}
else
{
int topValue = maxValue[i - 1][j]; // 上一个网格的值
int times = j / weight[i - 1];
int thisValue = 0;
if (times > 0)
{
for (int k = 1; k <= times; k++)
{
thisValue = value[i - 1] * k + maxValue[i - 1][j - weight[i - 1] * k] > thisValue ? value[i - 1] * k + maxValue[i - 1][j - weight[i - 1] * k] : thisValue;
}
}
// 返回 topValue和thisValue中较大的一个
maxValue[i][j] = (topValue > thisValue ? topValue : thisValue);
}
}
}
printVec2i(maxValue);
return maxValue[n][c];
}