Leetcoder 887. Super Egg Drop(动态规划)

题意:给K个鸡蛋,有N层楼,问至少要多少次扔鸡蛋才能保证找出摔破鸡蛋的临界楼层,鸡蛋碎了就没了。

思路:首先题意要搞清楚,是要保证能找出临界点,也就是你采取的方案无论临界点在哪一层,都必须在扔这么多次之内找出来。

方法一:dp[i][j]表示剩余i个鸡蛋处理j层楼至少要扔几次,那么怎么推理dp[i][j]呢?枚举第一个鸡蛋扔的楼层(1~j),假设扔到了第k层,碎了,转移到dp[i-1][k-1];没碎,转移到dp[i][j-k]。结果就是这两种情况,因为要保证找出临界点,我们必须假设最坏情况即max(dp[i-1][k-1], dp[i][j-1]),但是第一个鸡蛋我不一定扔在第K层,我选一个最优的楼层去扔也就是min(max(dp[i-1][k-1], dp[i][j-1])) (1<=k<=j),然后就搞定了,复杂度O(k*n*n),结果对但是会超时。

class Solution {
public:
    int dp[101][10001];
    int superEggDrop(int K, int N) {
        memset(dp, 0x3f, sizeof(dp));//初始化为无限大
        for(int i=1; i<=N; ++i) dp[1][i] = i;
        for(int i=0; i<=K; ++i) dp[i][0] = 0;
        for(int i=2; i<=K; ++i)
            for(int j=1; j<=N; ++j)
                for(int k=1; k<=j; ++k)
                    dp[i][j] = min(dp[i][j], max(dp[i][k-1], dp[i-1][j-k])+1);
        return dp[K][N];
    }
};

方法二:参考别人的牛逼思路,dp[i][j]表示i个鸡蛋扔j次至多能测出多少层的楼(换个说法:临界点已知在这么多层楼之内的话,i个蛋扔j次肯定能找出来)。怎么推dp[i][j]呢?也是考虑第一个蛋扔的位置,你会扔在哪里?假如扔在特别高的楼层,第10000000000......层,万一它碎了,它就白白的碎了,没有为剩下的i-1个蛋,j-1次操作作贡献。所以第一次扔的楼层应该是第dp[i-1][j-1]+1楼,为什么呢?因为这样即使它碎了,剩下的i-1个蛋肯定要往楼下扔,而dp[i-1][j-1]便是它们能发挥的最大作用楼层(物尽其用);如果它没碎肯定要往楼上扔,那就是dp[i][j-1]了。总结就是dp[i][j] = dp[i-1][j-1] + dp[i][j-1] + 1了,复杂度O(k*n)。这刚好是杨辉三角,容易整型溢出,编码注意for循环的顺序。

class Solution {
public:
    int dp[101][10001];
    int superEggDrop(int K, int N) {
        for(int j=1; j<=N; ++j){
            for(int i=1; i<=K; ++i){
                dp[i][j] = dp[i-1][j-1] + dp[i][j-1] + 1;
                if(dp[i][j] >= N)
                    return j;
            }
        }
        return N;
    }
};

总结:方法一和方法二的区别就是,方法一要自己枚举第一个蛋扔的楼层,方法二确定了第一个蛋扔的楼层,所以少了一层循环。

你可能感兴趣的:(普通dp)