This problem(为了确定这样一只猫能活下来的最大楼层,你需要从建筑物中扔出多少只猫。实际上非常残忍),已经接受了O(n ^ 3)的答案)复杂性。该问题等同于此Google Code Jam,对于N = 2000000000,该问题应该是可解的。
似乎O(n ^ 3)解决方案不足以解决它。 从查看in the solutions page开始,jdmetz的解决方案(#2)似乎是O(n log n)。 我不太了解算法。有人可以解释一下吗?
修改
答案 0 :(得分:8)
顶级解决方案实际上是O(D*B)
(使用问题的术语),但作者注意到,对于大多数D
和B
,答案将大于2^32
,因此{ {1}}可以退回
因此,他引入了等于1100的-1
- 最重要的maxn
和D
。
对于F
,他立即返回-1。对于D, B = 10000
,使用下面的递归公式。仅对于某些“角点值”,例如D, B = 100
,使用直接公式。 (有关“直接配方”的详细信息,请参阅他的评论)
对于D = 10000, B = 2
,作者在评论中提供了公式:D, B < maxn
。此处的函数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)
。
现在,它可以很容易地编码为DP(动态编程)。如果你离开无穷大(f(d-1,b)+f(d-1,b-1)+1
)检查,它会变得很干净。
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
单调增长
答案 1 :(得分: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(你必须从一楼,因为如果你从较高的楼层扔出去并且死亡,你就没有结果。)
现在,考虑函数g(x,y)= SUM 1&lt; = r&lt; = y of x C r 。 (这是二项式系数,x选择r。我不认为此站点支持TeX表示法)。断言是f(x,y)= g(x,y)。要检查基本情况,请考虑g(x,0)是0个元素的总和,以便匹配;和g(1,y),其中y> 0表示 1 C 1 = 1.因此,只需检查g是否满足复发。
g(x-1,y-1)+ g(x-1,y)+ 1 = SUM 1&lt; = r&lt; = y-1 x-1 C r + SUM 1&lt; = r&lt; = y x-1 C r + 1
g(x-1,y-1)+ g(x-1,y)+ 1 = SUM 2&lt; = r&lt; = y x-1 C r-1 + SUM 1&lt; = r&lt; = y x-1 C r + 1
g(x-1,y-1)+ g(x-1,y)+ 1 = SUM 2&lt; = r&lt; = y x-1 C r-1 + SUM 1&lt; = r&lt; = y x-1 C r + x-1 C 0 子>
g(x-1,y-1)+ g(x-1,y)+ 1 = SUM 1&lt; = r&lt; = y x-1 C r-1 + SUM 1&lt; = r&lt; = y x-1 C r
g(x-1,y-1)+ g(x-1,y)+ 1 = SUM 1&lt; = r&lt; = y( x-1 C r- 1 + x-1 C r )
g(x-1,y-1)+ g(x-1,y)+ 1 = SUM 1&lt; = r&lt; = y( x C r )
g(x-1,y-1)+ g(x-1,y)+ 1 = g(x,y)
QED
事实上,这可以通过将其视为组合问题直接推导出来。如果我们充分利用所获得的每一点信息,那么每个可能的生存或死亡序列对应于不同的最大楼层。例如。三次投掷,一次允许死亡,可能性是死亡;生存死亡;生存生存,死亡;或生存 - 生存 - 生存。但是,我们不得不打折没有死亡的情况,因为在这种情况下我们还没有确定底线。因此,如果我们有x次投掷和y允许死亡,我们可以有从1到y的实际死亡数r,并且对于每个我们有 x C r 的人序列。 (如果r = y那么y th 死亡后的任何“幸存者”实际上都是“没有扔掉”的。)
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)+ D C B 我们得到f(D,B)= 2 * f(D-1,B)+ 1 - D-1 C B 。然后我们可以使用 a C b = a-1 C b * a /(a - b)来从1循环到D。
private static int d_opt(final long f, final int b) { int d = 0, dCb = 0; long f_db = 0; while (f_db < f) { dCb = (d == b) ? 1 : d * dCb / (d-b); f_db = 2 * f_db + 1 - dCb; d++; } return d; }