Algorithm——高楼扔鸡蛋

问题描述

高楼扔鸡蛋问题是一道经典的动态规划问题,题目如下:

给你 k 枚相同的鸡蛋,并可以使用一栋从第 1 层到第 n 层共有 n 层楼的建筑。已知存在楼层 f ,满足 0 <= f <= n ,任何从 高于 f 的楼层落下的鸡蛋都会碎,从 f 楼层或比它低的楼层落下的鸡蛋都不会破。每次操作,你可以取一枚没有碎的鸡蛋并把它从任一楼层 x 扔下(满足 1 <= x <= n)。如果鸡蛋碎了,你就不能再次使用它。如果某枚鸡蛋扔下后没有摔碎,则可以在之后的操作中 重复使用 这枚鸡蛋。请你计算并返回要确定 f 确切的值 的 最小操作次数 是多少?

问题分析

这个问题理解起来还是不容易的,可以说只要把题目理解清楚了,这道题就没那么难了。根据题目,我们有k枚鸡蛋,n层楼。首先我们考虑两种特殊情况。第一种情况是假设只有1枚鸡蛋,n层楼,要想确定无疑地知道哪一层是f层(鸡蛋刚好不会碎的那层),就不能随便选择楼层扔,必须从1楼开始扔,才能确定f的值。因为你只有1枚鸡蛋,万一随便找了中间的一层扔下去鸡蛋碎了,那不就没有鸡蛋了吗,没有鸡蛋了自然也就无法确定f的值了。还有一种情况是n(楼层)为0的情况,当一层楼都没有的时候,自然一次也不需要扔,f的值为0。以上两种情况就确定了base case,即动态规划的基本状态。然后我们考虑如何建立状态转移方程。
建立状态转移方程之前,我们需要明白如何在状态之间进行选择。
我们求的原问题是:有k枚鸡蛋,n层楼,确定f。我们要做的第一步有n种选择,即从1……n层楼分别扔下去,而扔下去又有两种结果:鸡蛋碎了或者鸡蛋没碎。假设我们从第 i 层楼扔下去了,当鸡蛋碎了的时候,说明 f 在0 ~ i - 1层,当鸡蛋没碎的时候,说明 f 在 i ~ n层。那么问题来了,我们该如何选择呢?
1、 我们仅考虑在 i 层扔的情况,在鸡蛋碎了和鸡蛋没碎两种情况下进行考虑,由于总是考虑最坏情况,所以我们应该选择需要步数多的那种情况。

 max{dp[[k - 1,n - i],dp[k,i - 1]} + 1 // +1 代表在第i层扔的那一步

2、 确定了如何在单层选择之后,我们需要考虑如何在所有层之间选择(即1……n),这一步相较于上一步就很好理解了,因为题目求的是最小操作次数,所以应该取各层之间最小的值。


最后,得出状态转移方程为:
dp[k,n] = min{ max{dp[[k - 1,n - i],dp[k,i - 1]} + 1, dp[k,n] }, 1 <= i <= n

代码实现(Java)

class Solution {
    private int[][] memo;
    public int superEggDrop(int k, int n) {
        memo = new int[k + 1][n + 1];
        for( int i = 0; i < memo.length; ++i){
            for( int j = 0; j < memo[0].length; ++j ){
                if( i == 1 ){
                    memo[i][j] = j;
                }
                if( j == 0 ){
                    memo[i][j] = 0;
                }
                if( i != 1 && j != 0 ){
                    memo[i][j] = -1;
                }
            }
        }
        return dp(k,n);
    }

    private int dp(int k, int n){
        int res = Integer.MAX_VALUE;
        if( memo[k][n] != -1 ){
            return memo[k][n];
        }
        for( int i = 1; i <= n; ++i ){
            res = Math.min(res,Math.max(dp(k - 1,i - 1),dp(k,n - i)) + 1 );
        }
        memo[k][n] = res;
        return res;
    }
}

你可能感兴趣的:(java,dp,算法与数据结构,动态规划,算法,leetcode)