动态规划:背包问题
背包容积为 Weight
n 个物体,第 i 个物体的体积为 Wi,价值为 Vi
问:背包最大能够存放的物体价值之和最大为多少?
问题分析:
令 OPT(i, w) 表示前 i 个物体中,选择一些物体存放入容积为 w 的背包,所选中的所有物体的价值之和最大值。
在前 i 个物体,选择存放入背包的最大值之和,包括两种情况
第一种:第 i 个物体不放入背包
则一定是在 {1, 2, …, i - 1} 个物体中,可用容积为 w 的前提下,选择一些物体放入背包中,第 i 个物体不放入背包中。
OPT(i, w)= OPT(i - 1, w)
第二种:第 i 个物体放入背包
则一定是在 {1, 2, …, i - 1} 个物体中,可用容积为 w - Wi 的前提下,选择一些物体放入背包中,并且加入了第 i 个物体。
为什么是 w -Wi 的前提下?
因为要选择第 i 个物体放入背包,则选择第 i 个物体时,背包中剩余空间容积 >= 第 i 个物体的体积 Wi (否则第 i 个物体是无法放入背包的)
所以背包中现有物体总体积 <= w - Wi
OPT(i, w) = Vi + OPT(i - 1, w - Wi)
建立动态规划基本方程
if (i == 0)
{
OPT(i, w) = 0;
}
else if (Wi > w)
{
OPT(i, w) = OPT(i - 1, w);
}
else
{
OPT(i, w) = Max(OPT(i - 1, w), Vi + OPT(i - 1, w - Wi));
}
逻辑代码如下
class KnapsackProblem
{
// 物体个数
private const int Count = 5;
// 物体体积,创建 Count + 1 个空间的数组,第 i 个物体体积为 W[i]
private int[] W = new int[Count + 1] { 0, 1, 2, 5, 6, 7 };
// 物体价值,创建 Count + 1 个空间的数组,第 i 个物体价值为 V[i]
private int[] V = new int[Count + 1] { 0, 1, 6, 18, 22, 28 };
// 背包容积
private const int Weight = 11;
// 存储动态规划结果,OPT[Count, Weight] 为最终结果
private int[,] OPT = new int[Count + 1, Weight + 1];
public KnapsackProblem()
{
int result = Knapsack();
Console.WriteLine("result:" + result);
Print();
}
private int Knapsack()
{
// 初始化令物体个数为 0 时,选中物体价值 = 0
for (int w = 0; w <= Weight; ++w)
{
OPT[0, w] = 0;
}
// 物体个数遍历,从第 1 个物体开始
for (int i = 1; i <= Count; ++i)
{
// 容量大小遍历,从容量为 1 开始
for (int w = 1; w <= Weight; ++w)
{
// W[i] 第 i 个物体的体积
// V[i] 第 i 个物体的价值
if (W[i] > w)
{
OPT[i, w] = OPT[i - 1, w];
}
else
{
OPT[i, w] = Math.Max(OPT[i - 1, w], V[i] + OPT[i - 1, w - W[i]]);
}
}
}
return OPT[Count, Weight];
}
private void Print()
{
for (int i = 0; i <= Count; ++i)
{
for (int j = 0; j <= Weight; ++j)
{
Console.Write(OPT[i, j] + " ");
}
Console.WriteLine();
}
}
}
最终结果为
result = 40, 选择第 3 个和 第 4 个物体放入背包
下图为计算过程以及结果