高楼扔鸡蛋

100层楼2个鸡蛋,如何得知鸡蛋能承受几层的撞击。没太明白题意,google之。

1. 你有2个一摸一样的鸡蛋(所有性质相同)。
2. 有一幢100层的楼。注意即使是一楼和地面也有距离的。
3. 鸡蛋可能很硬也可能很软, 意思是有可能从一楼扔下来就碎了, 也有可能从100楼扔下来还不碎。
4. 你必须,是*必须*搞清楚最高从几楼扔下来鸡蛋是不会碎的。
5. 此过程中你被允许打破这2个鸡蛋。
6. 只准用从楼上扔鸡蛋的方式测试, 不能有任何别的辅助工具和手段。

微阅堂一个OI博客 都有提到过,曾经也和同学讨论过,这次看到有点蒙,想了一会想出来了。

不能用二分法,因为如果在50层碎了,下面的策略就是一层一层的试验了。一定有一个最低层L,碎了就从第一层测试到L-1层,没碎就往上走。

测试一次如果没碎,那么往上走的层数需要比上一次上楼的层数减1,上到100层时上楼的次数就应该等于L,这样无论在哪里第一个蛋碎了,两个蛋测试总次数都是相同的。

于是: L + (L-1) + ... + (L-L) = 100  =>  L*L - [1+2+...+(L-1)] = 100

解得: L = 13.x

最后验证:

14+13+12+11+10+9+8+7+6+5+4 = 99      用了11次.

13+12+11+10+9+8+7+6+5+4+3+2+1=91  不到100

结果是14。连程序都省了。


如果微阅堂的300层楼,3个鸡蛋呢?这个要用到动态规划(DP)。

有n层楼k个鸡蛋,则在第r层楼扔下鸡蛋只有破和不破两种情况,如果破了则问题转化为测定n-1层楼,k-1个鸡蛋所需的最小次数+1;如果不破则转化为测定n-r层楼,k个鸡蛋所需的最小次数。

公式表示:

F(n, k) = min ( max(F(n-r, k) , F(r-1, k-1)) + 1), r = 1, 2, …, n;
F(n, 1) = n; F(0, n) = 0

递归的程序:

#!/usr/bin/env python3
# -*- coding: utf-8 -*-

# Author: Yuande <miraclecome (at) gmail.com>

import sys

def memo(f):
    table = {}
    def fmemo(*args):
        if args not in table:
            table[args] = f(*args)
        return table[args]
    fmemo.memo = table
    return fmemo

@memo
def func(n, k):
    if k == 1: return n
    if n == 0: return 0
    least = n
    for r in range(1, n+1):
        keep = func(n-r, k) + 1
        broke = func(r-1, k-1) + 1
        step = max(keep, broke)
        if least > step:
            least = step
    return least

if __name__ == '__main__':
    ret = func(100, 2)
    print(ret)
    ret = func(300, 3)
    print(ret)
    sys.setrecursionlimit(15000)
    ret = func(900, 9)

问题:

   做了cache,貌似是吧时间复杂度从指数降到O(nk)级别了。递归的时间复杂度总是想不清楚。 

   大数据量时python有递归层数限制,无法计算,可以转换成C代码。但毕竟系统栈有限(貌似2M),用递归不是最好的方法。谁有更好的方法吗?


  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 4
    评论
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值