金字塔动态编程

时间:2015-08-21 02:04:12

标签: algorithm dynamic-programming

我在一次采访中遇到了这个问题而无法理解。我相信它有一个动态的编程解决方案,但它让我望而却步。

给定多个砖块,输出可能的2d金字塔总数,其中金字塔被定义为任何结构,其中一排砖块的砖块严重少于其下方的砖块。你不必使用所有砖块。

砖块只是一个正方形,连续砖块的数量是唯一重要的信息。

真的坚持这个,我认为很容易解决每个问题1 ... n迭代和总结。但是提出金字塔的数量正好可以让我回避。

示例,n = 6

X

XX

X
XX   XXX

X
XXX   XXXX

XX      X
XXX   XXXX   XXXXX

X
XX      XX       X
XXX   XXXX   XXXXX   XXXXXX

所以答案是来自6块砖的13个可能的金字塔。

修改

我很肯定这是一个动态编程问题,因为(一旦你确定了第一行)有意义只需查看你剩余砖块的记忆数组中的索引,看看有多少金字塔适合于顶部。

考虑底部行的宽度至少为n / 2也是有道理的,因为我们不能在底部行上面有更多砖块除了,这是我失去它和我的思想的地方在某些(很少的情况下),你可以分崩离析N = 10

X
XX
XXX
XXXX

现在底行有4个,但最左边有6个

但是当n = 11时,我们不能有一个低于n / 2砖的底行。还有另一种奇怪的不一致性,就像n = 4那样我们不能有一排n / 2 = 2砖。

4 个答案:

答案 0 :(得分:4)

让我们选择一个合适的定义:

f(n, m) = # pyramids out of n bricks with base of size < m

您现在正在寻找的答案是(假设N是您输入的砖块数量):

f(N, N+1) - 1

让我们打破这一点:

  • 第一个N显而易见:那就是你的砖块数量。
  • 您的底行最多包含N块(因为这就是您所拥有的所有块),因此N+1是一个足够的下限。
  • 最后,- 1就在那里,因为从技术上讲,空金字塔也是一个金字塔(因此会被计算在内),但你可以从解决方案中排除它。

基本案例很简单:

f(n, 0) = 1   for any n >= 0
f(0, m) = 1   for any m >= 0

在这两种情况下,这都是我们在这里计算的空金字塔。

现在,我们所需要的只是一般案例的递归公式。

我们假设我们获得了nm,并选择在底层使用i砖块。我们可以在这一层上放置什么?一个较小的金字塔,我们留下了n - i块砖,其底部的大小为< i。这正是f(n - i, i)

i的范围是多少?我们可以选择一个空行i >= 0。显然,i <= n因为我们只有n砖块。但是,i <= m - 1,根据m的定义。

这会导致递归表达式:

f(n, m) = sum f(n - i, i) for 0 <= i <= min(n, m - 1)

你可以递归地计算f,但是使用动态编程当然会更快。存储结果矩阵很简单,所以我把它留给你。

回到最初的声明f(N, N+1)-1是您正在寻找的答案,只要它是m,为> N选择哪个值并不重要。根据递归公式,可以很容易地显示每个f(N, N + 1) = f(N, N + k) k >= 1

f(N, N + k) = sum f(N - i, i) for 0 <= i <= min(N, N + k - 1)
            = sum f(N - i, i) for 0 <= i <= N
            = sum f(N - i, i) for 0 <= i <= min(N, N + 1 - 1)

答案 1 :(得分:3)

您可以通过多少种方式构建宽度 n 的金字塔?通过将任何宽度 n-1 或更小的金字塔放置在 n 砖层的顶部。因此,如果 p(n)是宽度 n 的金字塔数,那么 p(n)= sum [m = 1到n-1](p (m)* c(n,m)),其中 c(n,m)是你可以放置一层宽度 m 的方式的数量在一层宽度 n 之上(我相信你可以自己解决这个问题)。

然而,这并不限制砖的数量。通常,在DP中,必须将任何资源限制建模为单独的维度。所以你的问题现在是 p(n,b):&#34;你可以用 b 构建宽度 n 的金字塔数量>砖&#34;?在递归公式中,对于在当前建筑物上方构建较小金字塔的每种可能方式,您需要参考正确数量的剩余砖块。我把它作为一个挑战,让你找出递归公式;如果您需要任何提示,请告诉我。

答案 2 :(得分:2)

你可以将你的递归看作:在最后一行使用<!DOCTYPE html> <html> <head> <script src="bower_components/angular/angular.js"></script> <script src="app/app.js"></script> </head> <body ng-app="myApp" ng-controller="myController"> <ul ng-repeat="x in names"> <li> {{ x }} </li> </ul> </body> </html> 砖的地方留下了x砖块,你可以建造多少个金字塔。现在,您可以从上到下行或从下到上填充行。我会解释前一种情况 这里递归可能看起来像这样(n是剩下的砖数,left是最后一行使用的砖数)

last

因为当你在当前行上使用f(left,last)=sum (1+f(left-i,i)) for i in range [last+1,left] inclusive. 砖块时,你将剩下i块砖,而left-i将是此行上使用的砖块数。

代码:

i

我会留给您实施int calc(int left, int last) { int total=0; if(left<=0) return 0; // terminal case, no pyramid with no brick for(int i=last+1; i<=left; i++) { total+=1+calc(left-i,i); } return total; } memoized版本。另外,您可能希望从底行开始并填充金字塔中的上行。

答案 3 :(得分:2)

由于要求我们计算任何小于或等于it's id is 1 again的基数的金字塔,我们可以依次考虑每个基数(1个元素的金字塔,2个元素,3 ......等)并将它们相加起来。但是我们可以用多少种不同的方式组成n元素的金字塔?与k的不同分区数相同的数字(例如,对于k,我们可以有k = 6)。 Wikipedia中的OEISPentagonal number Theorem处的序列描述了不同分区计数的生成函数/重复。

重复,基于JSFiddle example

(6), (1,5), (2,4), and (1,2,3)

由于维基百科中描述的重复性要求计算所有前面的q(k) = ak + q(k − 1) + q(k − 2) − q(k − 5) − q(k − 7) + q(k − 12) + q(k − 15) − q(k − 22)... where ak is (−1)^(abs(m)) if k = 3*m^2 − m for some integer m and is 0 otherwise. (The subtracted coefficients are generalized pentagonal numbers.) 以获得更大的q(n),我们可以简单地对结果进行求和以获得我们的结果。

JavaScript代码:

q(n)