【妙】高楼扔鸡蛋的数学解法

关于高楼扔鸡蛋问题,本文只对动态规划方法进行概括性的描述,具体看这里

题目描述

K个鸡蛋 N 层楼,问至少尝试多少次可以找到鸡蛋不碎的临界楼层,求最少次数M

动态规划

方案1

设dp[K][N] 表示K个鸡蛋N层楼要尝试的最少次数
·
dp[K][N] = min ⁡ 1 ≤ X ≤ N \min_{1\le X \le N} min1XN(max(dp[K-1][X-1], dp[K][N-X]))
dp[K-1][X-1]递增,dp[K][N-X]递减,dp[K][N]相当于取谷底,可以二分

方案2

设dp[K][M] 表示K个鸡蛋尝试M次可以确定的最大层数
·
dp[K][M] = dp[K][M-1] + dp[K-1][M-1] + 1
dp[K][M-1] :鸡蛋没碎,楼下K个蛋尝试M-1次可以确定的最大层数
dp[K-1][M-1] :鸡蛋碎了,楼上K-1个蛋尝试M-1次可以确定的最大层数
·
可以进一步压缩空间,因为本次答案只和上次的M-1有关,可以压缩成一维的
dp[K] = dp[K] + dp[K-1] + 1

vector<vector<int>> dp(K+1, vector<int>(N+1, 0));
int m = 0;
while(dp[K] < N){
    m ++;
    for(int k=1; k<=K; k++)
        dp[k] = dp[k] + dp[k-1] + 1;
}
return m;

数学方法

观察上述方案2的方程,为了下面方便表示,换一下表示形式f(M,K) = dp[K][M]
g(m,k) = f(m,k)-f(m,k-1),则:
g ( m , k ) = f ( M , K ) − f ( M , K − 1 ) = f ( M − 1 , K ) + f ( M − 1 , K − 1 ) + 1 − ( f ( M − 1 , K − 1 ) + f ( M − 1 , K − 2 ) + 1 ) = g ( m − 1 , k ) + g ( m − 1 , k − 1 ) \begin{aligned} g(m,k) &= f(M,K)-f(M,K-1) \\ &= f(M-1,K) + f(M-1,K-1)+1-(f(M-1,K-1) + f(M-1,K-2)+1) \\ &= g(m-1,k)+g(m-1,k-1) \end{aligned} g(m,k)=f(M,K)f(M,K1)=f(M1,K)+f(M1,K1)+1(f(M1,K1)+f(M1,K2)+1)=g(m1,k)+g(m1,k1)

??? 是否有些许熟悉,杨辉三角???

A r , c A_{r,c} Ar,c 表示杨辉三角中第 r 行第 c 列的元素,它的值又是二项式系数 C r c C_{r}^{c} Crc ,即: A r , c = A r − 1 , c + A r − 1 , c − 1 = C r c A_{r,c} = A_{r-1,c} + A_{r-1,c-1} = C_{r}^{c} Ar,c=Ar1,c+Ar1,c1=Crc

所以 :
g ( m , k ) = C m k + 1 f ( m , k ) = ∑ 1 ≤ x ≤ K g ( m , x ) = ∑ C m x \begin{aligned} g(m,k) &= C_m^{k+1} \\ f(m,k) &= \sum_{1 \le x \le K}g(m,x) = \sum C_{m}^{x} \end{aligned} g(m,k)f(m,k)=Cmk+1=1xKg(m,x)=Cmx

从另一种角度理解这个公式

k个蛋尝试m次,每次选楼层的的结果就是碎或不碎(1/0),所以不管选那一层,最后的方案就是一个长度为m的01序列,碎了0次的方案数就是 C m 0 C_m^0 Cm0,碎了一次的就是 C m 1 C_m^1 Cm1……,总方案数就是 ∑ 1 ≤ x ≤ K C m x \sum_{1\le x \le K}C_m^x 1xKCmx

现在的目标转化为找最小的 m 使得 f ( m , K ) ≥ N f(m,K) \ge N f(m,K)N ,可以二分寻找 m

class Solution {
    public int superEggDrop(int K, int N) {
        int l = 1, r = N;
        while (l < r) {
            int m = l + (r - l >> 1);
            if (f(m, K, N) < N) l = m + 1;
            else r = m;
        }
        return l;
    }

    public int f(int x, int K, int N) {
        int ans = 0, r = 1;
        for (int i = 1; i <= K; ++i) {
            r *= x-i+1;
            r /= i;
            ans += r;
            if (ans >= N) break;
        }
        return ans;
    }
}

参考:

https://leetcode.com/articles/super-egg-drop/
https://zhuanlan.zhihu.com/p/92288604
https://zhuanlan.zhihu.com/p/95172244

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值