887. Super Egg Drop

解法

不是解法的生产者,只是官方解法的搬运工= =

官方解法:https://leetcode.com/problems/super-egg-drop/solution/
总共有两大类解法,一种是基于常规DP/递归思路的,把问题不断拆成子问题;另一种是反其道行之,考虑在K个鸡蛋的情况下,t次操作最多能在多少层楼的情况下确定F,找到最小的t即可

基于DP/递归

我们假设给定K,N的时候的解为 T ( K , N ) T(K,N) T(K,N),假设第一次从x楼扔下来,那么显然:

  • 如果鸡蛋没碎,F应该在x楼以上,这时候我们还有K个鸡蛋,所以问题变成从x+1,...,N=x+(N-x)楼找F的最小步数,显然它为T(K,N-X)
  • 如果鸡蛋碎了,那么我们还有K-1个鸡蛋,此时确定F在x以下,问题变成从1,...,x-1楼确定F,即T(K-1,x-1)
    综上我们得到递归方程式:
    T ( K , N ) = 1 + min ⁡ 1 ≤ x ≤ N { max ⁡ { T ( K − 1 , x − 1 ) , T ( K , N − x ) } } T(K,N)=1+\min_{1\le x\le N}\{\max\{T(K-1, x-1), T(K, N-x)\}\} T(K,N)=1+1xNmin{max{T(K1,x1),T(K,Nx)}}
    但是直接这么做会超时,所以有两种提高效率的方法,它们都基于一个思想,那就是,当K固定时,T(K,N)随N递增:

方法一:二分查找

当K和N都固定时,我们令 T 1 ( x ) = T ( K − 1 , x − 1 ) T_1(x)=T(K-1, x-1) T1(x)=T(K1,x1) T 2 ( x ) = T ( K , N − x ) T_2(x)=T(K, N-x) T2(x)=T(K,Nx),那么:
T ( K , N ) = 1 + min ⁡ 1 ≤ x ≤ N { max ⁡ { T 1 ( x ) , T 2 ( x ) } } T(K,N)=1+\min_{1\le x\le N}\{\max\{T_1(x), T_2(x)\}\} T(K,N)=1+1xNmin{max{T1(x),T2(x)}}
可以看出,T1随x递增,而T2随x递减。所以 max ⁡ 1 ≤ x ≤ N { T 1 ( x ) , T 2 ( x ) } \max_{1\le x\le N}\{T_1(x), T_2(x)\} max1xN{T1(x),T2(x)}就是蓝色部分,所以可以通过二分查找找到最小值所在的地方。
1
注意: 因为交点的位置可能不是整数,所以最小值在交点两边最近的两个整数点处产生,这时候要比较这两个点,所以二分查找的区间要大于2个点。

class Solution(object):
    def superEggDrop(self, K, N):
        """
        :type K: int
        :type N: int
        :rtype: int
        """

        dp = {}

        def get(k,n):
            if (k,n) not in dp:
                if k==1:
                    return n
                elif n==0:
                    return 0
                else:
                    l = 1
                    r = n
                    while r - l > 1:
                        mid = (l + r) >> 1
                        t1 = get(k-1, mid-1)
                        t2 = get(k, n-mid)
                        if t1>t2:
                            r = mid
                        elif t2>t1:
                            l = mid
                        else:
                            l = r = mid
                    dp[(k,n)] = 1+min(max(get(k-1,l-1), get(k,n-l)),max(get(k-1,r-1), get(k,n-r)))
            return dp[(k,n)]


        return get(K,N)

方法二:动态规划

上面我们得到了:
T ( K , N ) = 1 + min ⁡ 1 ≤ x ≤ N { max ⁡ { T ( K − 1 , x − 1 ) , T ( K , N − x ) } } = 1 + min ⁡ 1 ≤ x ≤ N { max ⁡ { T 1 ( x ) , T 2 ( x ) } } \begin{matrix} T(K,N)&=1+\min_{1\le x\le N}\{\max\{T(K-1, x-1), T(K, N-x)\}\}\\ &=1+\min_{1\le x\le N}\{\max\{T_1(x), T_2(x)\}\} \end{matrix} T(K,N)=1+min1xN{max{T(K1,x1),T(K,Nx)}}=1+min1xN{max{T1(x),T2(x)}}
主要是以x为自变量,当我们以N为自变量的时候发现,只有T2会随N的增大而递增,如图所示,可以看出最优值的交点越来越大了,所以可以用DP来解法,先求出小的N的值,然后再通过它去求大的N的值。
注意T1不变是在K不变的情况下!
所以,当K固定时,我们已经求出了T(K,N-1),使得T(K,N-1)最优的x为x',那么我们有:
T ( K , N ) = 1 + min ⁡ x ′ ≤ x ≤ N { max ⁡ { T ( K − 1 , x − 1 ) , T ( K , N − x ) } } T(K,N)=1+\min_{x'\le x\le N}\{\max\{T(K-1, x-1), T(K, N-x)\}\} T(K,N)=1+xxNmin{max{T(K1,x1),T(K,Nx)}}
2
事实上,假如我们令 T N ( x ) = max ⁡ { T ( K − 1 , x − 1 ) , T ( K , N − x ) } T_N(x) = \max\{T(K-1, x-1), T(K, N-x)\} TN(x)=max{T(K1,x1),T(K,Nx)},TN是个随着x先递减后递增的函数,它的最小值一定在比x'大的第一个使得 T N ( x ) &lt; T N ( x + 1 ) T_N(x)&lt;T_N(x+1) TN(x)<TN(x+1)的地方,更大的x就不用遍历了

class Solution(object):
    def superEggDrop(self, K, N):
        """
        :type K: int
        :type N: int
        :rtype: int
        """

        dp = range(N+1) # dp[n] = T(1,n)

        for k in xrange(2, K+1):
            dp_new = [0] # dp_new[n] = T(k,n), dp[n] = T(k-1, n)
            x = 1
            for n in xrange(1, N+1):
                # dp[x-1] = T(k-1, x-1) = T1(x)
                # dp_new[n-x] = T(k, n-x) = T2(x)
                # max(dp[x-1], dp_new[n-x]) = TN(x)
                while x<n and max(dp[x-1], dp_new[n-x])>max(dp[x],dp_new[n-x-1]):
                    x += 1
                dp_new.append(1+max(dp[x-1], dp_new[n-x]))
            dp = dp_new

        return dp[-1]

反其道而行之

这个真的强无敌= =
我们不枚举楼的高度,我们枚举操作的次数,并想知道在t次操作,K个鸡蛋的情况下,能确定F的最大楼层高度是多少,假设它为 f ( t , K ) f(t,K) f(t,K),很明显 f ( t , K ) f(t,K) f(t,K)随t的增加而增大,因为操作次数越多,能确定的楼层数肯定越多,所以我们需要找到最小的一个t'使得, f ( t ′ , K ) &gt; = N f(t&#x27;,K)&gt;=N f(t,K)>=N
然后我们要得到f(t,k)的递推关系式,假设有n层楼:

  • 当在楼层x做了一次操作之后,假如鸡蛋碎了,说明这次操作的楼层较高,我们需要在x-1层楼里通过t-1次操作和k-1个鸡蛋来确定F,所以有: f ( t − 1 , k − 1 ) ≥ x − 1 f(t-1,k-1)\ge x-1 f(t1,k1)x1
  • 当在楼层x做了一次操作之后,假如鸡蛋没碎,说明这次操作的楼层较低,我们需要在n-x层楼里通过t-1次操作和k个鸡蛋来确定F,所以有: f ( t − 1 , k ) ≥ n − x f(t-1,k)\ge n-x f(t1,k)nx
    将两个条件加起来我们可以消去x,得到: f ( t − 1 , k − 1 ) + f ( t − 1 , k ) ≥ n − 1 f(t-1,k-1)+f(t-1,k)\ge n-1 f(t1,k1)+f(t1,k)n1,也即: 1 + f ( t − 1 , k − 1 ) + f ( t − 1 , k ) ≥ n 1+f(t-1,k-1)+f(t-1,k)\ge n 1+f(t1,k1)+f(t1,k)n
    f ( t , k ) f(t,k) f(t,k)是n的最大值,所以有:
    f ( t , k ) = 1 + f ( t − 1 , k ) + f ( t − 1 , k − 1 ) f(t,k) = 1 + f(t-1, k)+f(t-1, k-1) f(t,k)=1+f(t1,k)+f(t1,k1)
    边界值为:
    f ( 1 , k ) = 1 , f ( t , 1 ) = t , t ≥ 1 , k ≥ 1 f(1,k)=1,f(t,1)=t, t\ge 1, k\ge 1 f(1,k)=1,f(t,1)=t,t1,k1
    3
    得到递推表达式之后,我们就有:
    f ( t , k ) = 1 + f ( t − 1 , k ) + f ( t − 1 , k − 1 ) f ( t , k − 1 ) = 1 + f ( t − 1 , k − 1 ) + f ( t − 1 , k − 2 ) \begin{matrix} f(t,k) = 1 + f(t-1, k)+f(t-1, k-1)\\ f(t,k-1) = 1 + f(t-1, k-1)+f(t-1, k-2) \end{matrix} f(t,k)=1+f(t1,k)+f(t1,k1)f(t,k1)=1+f(t1,k1)+f(t1,k2)
    将上式相减,得到:

f ( t , k ) − f ( t , k − 1 ) = [ f ( t − 1 , k ) − f ( t − 1 , k − 1 ) ] + [ f ( t − 1 , k − 1 ) − f ( t − 1 , k − 1 ) ] f(t,k)-f(t,k-1) = [f(t-1,k)-f(t-1,k-1)]+[f(t-1,k-1)-f(t-1,k-1)] f(t,k)f(t,k1)=[f(t1,k)f(t1,k1)]+[f(t1,k1)f(t1,k1)]
g ( t , k ) = f ( t , k ) − f ( t , k − 1 ) , t ≥ 1 , k ≥ 2 g(t,k) = f(t,k)-f(t,k-1), t\ge 1,k\ge 2 g(t,k)=f(t,k)f(t,k1),t1,k2,边界值为: g ( 1 , k ) = 0 , g ( t , 2 ) = ( t − 1 ) t 2 g(1,k) = 0, g(t,2) = \frac{(t-1)t}{2} g(1,k)=0,g(t,2)=2(t1)t
则有:
g ( t , k ) = g ( t − 1 , k ) + g ( t − 1 , k − 1 ) , t ≥ 2 , k ≥ 3 g(t,k) = g(t-1,k)+g(t-1,k-1), t\ge 2,k\ge 3 g(t,k)=g(t1,k)+g(t1,k1),t2,k3

观察一下g(t,k)的递推表达式,是不是有点像集合的表达式?对照一些边界值我们可以归纳出:
g ( t , k ) = C t k g(t,k) = C^{k}_{t} g(t,k)=Ctk
那么由于 f ( t , k ) = f ( t , k − 1 ) + C t k f(t,k)=f(t,k-1)+C^{k}_t f(t,k)=f(t,k1)+Ctk,最终我们得到:
f ( t , k ) = ∑ k ′ = 1 k C t k f(t,k)=\sum_{k&#x27;=1}^{k}C^k_t f(t,k)=k=1kCtk
最后用二分查找找到最近的t即可

class Solution(object):
    def superEggDrop(self, K, N):
        """
        :type K: int
        :type N: int
        :rtype: int
        """

        l = 1
        r = N
        while r>l:
            mid = (r+l)>>1
            f = 1
            c = 1
            for x in xrange(1,K):
                c = c* (mid-x+1)/x
                f += c
            if f>=N:
                r = mid
            else:
                l = mid+1
        return r
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值