【问题标题】:Explain this O(n log n) algorithm for the Cat/Egg Throwing Problem解释投掷猫/蛋问题的 O(n log n) 算法
【发布时间】:2011-06-09 14:27:30
【问题描述】:

This problem(您需要将多少只猫扔出建筑物才能确定这种猫可以存活的最大楼层。实际上非常残忍),具有 O(n^3) 复杂度的公认答案.这个问题等价于这个Google Code Jam,对于N=2000000000应该是可以解决的。

看来O(n^3)的解法还不够好解决。 从in the solutions page 看,jdmetz 的解决方案(#2)似乎是 O(n log n)。 我不太了解算法。谁能解释一下?

编辑

【问题讨论】:

  • 算法的解释对我来说似乎是 StackOverflow 的一个合适的问题。
  • 一旦启动就可以迁移到算法中:area51.stackexchange.com/proposals/5120/…
  • 如果这个算法是O(n*logn)(不管你用n表示三个变量中的哪一个),它仍然太慢了。 n 最高可达 10^9。
  • 如果您看到我对答案的最新更新,我可以做得比 O(n lg n) 更好。

标签: algorithm


【解决方案1】:

最佳解决方案实际上是O(D*B)(使用问题的术语),但作者注意到对于大多数D 和B 答案将大于2^32,因此可以返回-1。
因此,他介绍了等于 1100 的 maxn - 可以计算的最大值 D 和 F。
对于D, B = 10000,他立即返回-1。对于D, B = 100,使用下面的递归公式。仅对于某些“角值”,例如D = 10000, B = 2,使用直接公式。 (有关“直接公式”的详细信息,请参阅他的 cmets)

对于D, B < maxn,作者在cmets 中提供了公式:f(d,b) = f(d-1,b)+f(d-1,b-1)+1。函数f 在这里返回您可以使用不超过d 尝试和不超过b 鸡蛋确定“断点”的最大楼层数。
公式本身应该是不言自明的:无论我们在哪一层扔第一个鸡蛋,都有两种结果。它可能会坏掉,然后我们可以检查到下面的f(d-1, b-1) 楼层。或者它可以“生存”,然后我们可以检查到上面的f(d-1, b) 楼层。以当前楼层计算,总计为f(d-1,b)+f(d-1,b-1)+1。

现在,它可以很容易地编码为 DP(动态编程)。如果您离开无穷大 (2^32) 结帐,它会变得非常干净。

    for (int i = 1; i < maxn; ++i) {
        for (int j = 1; j < maxn; ++j) {
            f[i][j] = f[i - 1][j - 1] + f[i - 1][j] + 1;
        }
    }

当我们有数组f[D][B] 存储这些结果时,查找D' 和B' 是一种二分搜索。 (因为函数f 单调增长d 和b)

PS 虽然我在回答“猫”问题时确实说过有一个更快的解决方案,但这实际上比我想象的要酷 :) 感谢您和 sclo(作者)!

【讨论】:

  • 这仍然不够好,但可以调整。我实现了以下几个优化: 1. 只从 b=2 迭代,而不是 b=1。 2. 一旦溢出 maxint 就停止迭代,并保存一个映射 maxBForD(对于给定的 D,不溢出的最大 B)。 3. 保存 maxDForB2(对于 B>=2 不会溢出的最大 D)。最后,这可行:) github 上提供的代码:github.com/ripper234/Basic/blob/master/java/src/main/java/org/…
  • 这没有回答(更改的)问题,该问题已被修改为参考 jdmetz 的解决方案。
【解决方案2】:

由于问题得到纠正而进行了大量编辑

考虑函数 f(x,y),它给出了您可以在 x 次投掷和 y 次死亡的限制下测试的楼层数。如果第一次投掷导致死亡,则剩下 x-1 次投掷和 y-1 次死亡,因此您可以检查 f(x-1, y-1) 个楼层。如果第一次投掷没有导致死亡,那么您还有 x-1 次投掷和 y 次死亡,因此您可以检查您投掷的楼层上方的 f(x-1,y) 个楼层。因此我们有递归 f(x,y) = f(x-1, y-1) + f(x-1, y) + 1。基本条件是 f(x,0) = 0(因为如果你即使投掷一次也有死亡的风险,所以你不能投掷,甚至不能检查一楼),并且 f(1,x) = 1 其中 x>0 (你必须从一楼,因为如果你从更高的楼层扔掉并死亡,你没有结果)。

现在,考虑 xCr 的函数 g(x, y) = SUM 10 给出 1C1 = 1。所以只需要检查 g 是否满足递归。

g(x-1, y-1) + g(x-1, y) + 1 = SUM 1x-1Cr + SUM 1x-1Cr + 1

g(x-1, y-1) + g(x-1, y) + 1 = SUM 2x-1Cr-1 + SUM 1x-1Cr + 1

g(x-1, y-1) + g(x-1, y) + 1 = SUM 2x-1Cr-1 + SUM 1x-1Cr + x-1C0子>

g(x-1, y-1) + g(x-1, y) + 1 = SUM 1x-1Cr-1 + SUM 1x-1Cr

g(x-1, y-1) + g(x-1, y) + 1 = SUM 1x-1Cr- 1 + x-1Cr)

g(x-1, y-1) + g(x-1, y) + 1 = SUM 1xCr)

g(x-1, y-1) + g(x-1, y) + 1 = g(x, y)

QED

事实上,这可以通过将其视为组合问题直接推导出来。如果我们充分利用所获得的每一点信息,那么每个可能的生存或死亡序列对应于不同的最大底线。例如。三掷,一次允许死亡,可能性是死亡;生存-死亡;生存-生存-死亡;或生存-生存-生存。但是,我们必须不考虑没有死亡的情况,因为在那种情况下,我们还没有确定下限。因此,如果我们有 x 次投掷和 y 次允许死亡,我们可以得到从 1 到 y 的实际死亡人数 r,并且对于其中的每一个,我们都有 xCr 可能序列。 (如果 r = y,那么在第 yth 次死亡之后的任何“幸存者”实际上都是“未投掷”)。

jdmetz 对 F 的解决方案包括评估 g(D, B)。它不能在 O(min(D, B)) 时间内完成,因为该总和没有封闭的超几何形式(可以使用 Gosper 算法证明这一事实)。

附录 事实上,原始问题的所有三个部分都可以在线性时间内完成(假设乘法和算术是恒定时间的运算,我们到目前为止 - 不正确,但我们将把它放在一边)。 jdmetz 解决方案中的 O(n lg n) 操作是找到最小的 D 使得 f(D, B) >= F。

如果我们将原始递归 f(D, B) = f(D-1, B-1) + f(D-1, B) + 1 与 g 形式的项差异相结合,f(D , B) = f(D, B-1) + DCB 我们得到 f(D, B) = 2 * f(D-1, B) + 1 - D-1CB。然后我们可以使用 aCb = a-1Cb * a / (a - b) 来从 1 循环遍历 D。

 private static int d_opt(final long f, final int b)
    {
        int d = 0,dCb = 0;
        长 f_db = 0;
        而 (f_db 
    

【讨论】:

  • 抱歉,我看错文件了。看看 jdmetz 的解决方案 - getMaxF() 和 getMinB() 的时间为 O(n)。 getMinD() 是使用 getMaxF() 的二进制搜索实现的 - 总 O(n log n)
  • 我认为 F 可能与帕斯卡三角形有关,现在我知道我是对的。仍然没有证明它,但它是 jdmetz 的代码所做的。第一个观察:如果 j > i 则 F[i][j] = F[i][i],因为你不能比被扔的猫多死。因此我们可以只考虑由 i>=0, 0oeis.org/wiki/Bernoulli%27s_triangle),帕斯卡三角形行的部分和。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-12-12
  • 1970-01-01
  • 2015-06-12
  • 1970-01-01
  • 2011-12-11
相关资源
最近更新 更多