【问题标题】:Stacking and dynamic programing堆叠和动态规划
【发布时间】:2017-06-23 14:29:20
【问题描述】:

基本上我正在尝试解决这个问题:

给定 N 个单位的立方体块,为了使用所有块,找出要堆的较少数量的堆。一堆是立方体或金字塔。例如,两个有效的堆是立方体 4 *4 *4=64 使用 64 块,金字塔 1²+2²+3²+4²=30 使用 30 块。

但是,我找不到合适的角度来接近它。感觉和背包问题很像,但是还没有找到实现。

任何帮助将不胜感激!

【问题讨论】:

  • 我不明白这个问题。包装物品是二维正方形还是立方体?请更具体地说明输入的外观以及它与所需输出的关系。
  • 已编辑。抱歉缺乏精确度,它们是立方体。一个例子是:要储存 38 个积木,我们只需要两堆:例如,一个高度为 2 的立方体(容纳 8 个积木)和一个高度为 4 的金字塔(容纳 30 个积木)。
  • 根据this发布,已经NP-hard来决定一组方格是否可以打包成一个方格;我想问题中的问题也是如此。
  • 我认为有一些误解 - 如果输入的所有项目都是相同大小的,即边长为 1?
  • 是的,它们的长度确实都是 1。

标签: dynamic-programming combinatorics knapsack-problem


【解决方案1】:

首先我将给出一个递归关系,它允许递归地解决问题。给定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
}

【讨论】:

  • 例如,进行回溯以找到最佳分区的堆的想法是什么?
猜你喜欢
  • 2017-05-12
  • 2021-08-05
  • 2016-12-26
  • 2015-03-24
  • 2018-06-11
  • 1970-01-01
  • 2010-12-21
  • 2016-07-21
相关资源
最近更新 更多