首先我将给出一个递归关系,它允许递归地解决问题。给定N,让
SQUARE-NUMS
TRIANGLE-NUMS
分别是{1,...,N} 中平方数和三角形数的子集。让PERMITTED_SIZES 成为这些的联合。请注意,由于1 出现在PERMITTED_SIZES 中,因此任何实例都是可行的并且会产生非负最优值。
伪代码中的以下函数将递归解决问题中的问题。
int MinimumNumberOfPiles(int N)
{
int Result = 1 + min { MinimumNumberOfPiles(N-i) }
where i in PERMITTED_SIZES and i smaller than N;
return Result;
}
这个想法是为项目选择一个允许的 bin 大小,删除这些项目(这使得问题实例更小)并递归解决更小的实例。为了使用动态规划来规避对同一子问题的多次评估,可以使用一维状态空间,即数组A[N],其中A[i] 是i 单元块所需的最小堆数。使用这个状态空间,可以迭代地解决这个问题,如下所示。
for (int i = 0; i < N; i++)
{
if i is 0 set A[i] to 0,
if i occurs in PERMITTED_SIZES, set A[i] to 1,
set A[i] to positive infinity otherwise;
}
这会初始化预先知道的状态,并且对应于上述递归中的基本情况。接下来,使用以下循环填充缺失的状态。
for (int i = 0; i <= N; i++)
{
if (A[i] is positive infinity)
{
A[i] = 1 + min { A[i-j] : j is in PERMITTED_SIZES and j is smaller than i }
}
}
所需的最佳值将在A[N] 中找到。注意这个算法只计算最小的堆数,而不是堆本身;如果需要合适的分区,则必须通过回溯或维护额外的辅助数据结构来找到它。
总的来说,只要知道PERMITTED_SIZES,问题就可以在O(N^2)的步骤中解决,因为PERMITTED_SIZES最多包含N的值。
这个问题可以看作是对Rod Cutting Problem 的改编,其中每个正方形或三角形大小的值都为0,而其他所有大小的值都为1,目标是最小化总值。
总的来说,从输入生成PERMITTED_SIZES 需要额外的计算成本。
更准确地说,当A被填满后,可以使用回溯生成相应的桩选择,如下所示。
int i = N; // i is the total amount still to be distributed
while ( i > 0 )
{
choose j such that
j is in PERMITTED_SIZES and j is smaller than i
and
A[i] = 1 + A[i-j] is minimized
Output "Take a set of size" + j; // or just output j, which is the set size
// the part above can be commented as "let's find out how
// the value in A[i] was generated"
set i = i-j; // decrease amount to distribute
}