文章目录
背包问题是动态规划算法的一个典型实例。
1. 01背包问题
1.1 问题描述与算法分析
01背包问题描述:有编号分别为a,b,c,d,e的五件物品,它们的重量分别是2,2,6,5,4,它们的价值分别是6,3,5,4,6,每件物品数量只有一个,现在给你个承重为10的背包,如何让背包里装入的物品具有最大的价值总和?
动态规划的基本思路:将该问题转换成子问题,考虑五件物品在给定承重 C 的背包下最大价值为原问题,如下表所示,即为考虑abcde,C = 10时的最大价值,假设为f[5][10],原问题的解可以分解为两种情况,第一种情况是不考虑放入a只考虑放入bcde承重为C时的最大价值f[4][C],第二种情况是考虑放入a时的最大价值,即value[a]+f[4][10-weight[a]]。 原问题的解f[5][10]取上述两种情况中的最大值,即f[5][10] = max{f[4][10], (f[4][10-weight[a]+value[a]))}。 由此可以看出里面涉及到需要计算f[4][10]和f[4][10-weight[a]]即f[4][4]等子问题。 以此类推,自顶向下的分析可以看出原问题需要子问题的解,我们需要先计算出子问题的解,自底向上求解。求解方式如下表所示,顺序是自底向上、从左往右,或者从左往右、自底向上都可以。注意此问题中的abcde可以包含相同的物件,它们之间的顺序也可以是任意的,不影响最终的结果。
1.2 核心代码
vector<vector<int>>& knapsack01(
vector<pair<int, int>> &goodsVec, int packageVol)
{
if (goodsVec.empty())
{
throw "Invalid arg : goodsVec empty";
}
if (packageVol < 0 || packageVol > 1000)
{
throw "Invalid arg : packageVol out of range";
}
//动态规划的二维数组
static vector<vector<int>>dpValue(
goodsVec.size(), vector<int>(packageVol + 1, 0));
for (unsigned i = 0; i < dpValue.size(); ++i)
{
for (unsigned j = 0; j < dpValue[0].size(); ++j)
{
int volume = j;
if (0 == i) //只有一种物品时
{
dpValue[i][j] = goodsVec[i].first <= volume ? goodsVec[i].second : 0;
}
else if (0 == j) //背包容量为0时
{
dpValue[i][j] = 0;
}
else //背包容量和物品种数都>=1时,使用递推公式
{
//背包容量volume不能容纳一件物品weight[i]
if (volume < goodsVec[i].first)
{
dpValue[i][j] = dpValue[i - 1][j];
}
else //背包容量volume能容纳一件物品weight[i]
{
int value = dpValue[i - 1][volume - goodsVec[i].first]
+ goodsVec[i].second;
dpValue[i][j] = max(dpValue[i - 1][j], value);
}
}
} //for 2
} //for 1
return dpValue;
}
1.3 测试代码
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
void printDPValue(vector<vector<int>> &dpValue)
{
if (dpValue.empty())
{
throw "Invalid arg : dpValue empty";
}
for (unsigned i = 0; i < dpValue.size(); ++i)
{
for (unsigned j = 0; j < dpValue[0].size(); ++j)
{
cout << dpValue[i][j] << " ";
}
cout << endl;
}
}
//此处用上述核心代码替换
vector<vector<int>&