一、问题定义
有一个包含n个元素{e1, e2, …, en}的集合S,每个元素ei都有一个对应的权值wi。现在有一个界限W,我们希望从S中选择出部分元素,使得这些元素的权值之和在不超过W的情况下达到最大,这个便是子集合问题(事实上还有其他类型的子集和问题,本文暂不讨论)。举个更具体一点的例子,某农民今年收成小番茄总重量为W万斤,有n个采购商想要向这位农民收购小番茄,他们想要采购的数目都有所不同,采购商i想要收购wi万斤小番茄(1 <= i <= n)。现在农民需要从采购商中挑选部分买家,将小番茄卖给他们,使得自己被收购的小番茄数目达到最大,从而赚取最大的收入(假设所有采购商给出的单位收购价都是一样的;所有采购商的收购量总和超过农民的收成量,即农民无法满足所有采购商;收购商i想要收购wi万斤小番茄,他不会只收购一半或者更多)。
二、解决思路
根据动态规划的思路,我们来分析一下这个问题的子问题。假设农民已经从前面n-1个收购商中选出了一组最优组合使得收购量最大,现在他要考虑是否要卖给最后一个收购商n。假如卖给收购商n,那么他能够卖给前面n-1个收购商的番茄就只有(W-wn)万斤。如果他不卖给收购商n,那么他能够卖给前面n-1个收购商的番茄就是W万斤了。于是,假设在只有(W-wn)万斤的情况下,从前面n-1个收购商中选出最优组合所收购的总重量加上卖给收购商n的重量为(O1+wn)。若W万斤都卖给前面n-1个收购商,他们之中选出的最优组合所收购的总重量为O2。农民需要考虑的问题就变成了比较(O1+wn)和O2的大小了。
我们更形式化一点地进行描述,假设O(i, w)表示将w万斤小番茄提供给收购商{1, 2, …, i}收购的时候,从这i个收购商中选出最优组合所收购的总重量。那么O1 = O(n-1, W-wn),O2 = (n-1, W)。当农民考虑收购商n的时候,他需要判定O1和O2的大小。另外一种特殊情况,当收购商收购的数量wn超过农民拥有的所有小番茄的时候,即W < wn,那么农民自然只能考虑前面n-1个收购商中的最优组合了。
更进一步考虑,当我们考虑O(i, w)的时候,如果w能够容纳wi,那么我们需要考虑O(i-1, w)和(O(i-1, w-wi) + wi)的大小。如果w无法容纳wi,即w < wi,那么无需考虑i,O(i, w) = O(i-1, w)。因此,我们可以得到递推公式如下所示:
if w < wi, O(i, w) = O(i-1, w)
else O(i, w) = max(O(i-1, w), wi + O(i-1, w-wi))
得到了递推公式,我们自然就可以得到一个算法来算出最优解。算法的伪代码如下所示:
数组O[0...n, 0...W]