887. Super Egg Drop

查看题目

谷歌当年的面试题的加强版,仿佛脑筋急转弯

一步一步来,要求的是最小的 最大步数。最大是指,在最坏情况下,求得F需要的步数;最小是指,在各种策略下,所能求出的最小的最大值。

优秀的dp走起来,那最优值即最小的最大步数,状态dp[n][k]表示n层k蛋的最优值
接下来,求dp[n][k]时,假设一个鸡蛋从第i层扔下,有两种情况:

  1. 碎了。碎了不能再用。那接下来测i之下的层数,即dp[n][k] = 1+dp[i-1][k-1]
  2. 没碎。没碎还能再用。接下来测的是i之上得到层数,即dp[n][k] = 1+dp[n-i][k]

之后我们再遍历每个i,求出最小的
状态转移方程为dp[n][k] = min(max(dp[n-i][k], dp[i-1][k-1]))

    int superEggDrop(int K, int N) {
        int dp[N+2][K+2];
        
        for (int i = 0; i <= N; i ++) {
            dp[i][0] = 0;
            dp[i][1] = i;
        }
        for (int i = 0; i <= K; i ++) {
        	dp[0][i] = 0;
        }
        
        for (int k = 2; k <= K; k ++) {
            for (int n = 1; n <= N; n ++) {
                dp[n][k] = n;
                for (int i = 1; i < n; i ++) {
                    int rst = max(dp[n-i][k], dp[i-1][k-1]) + 1;
                    if (rst < dp[n][k]) dp[n][k] = rst;
                }
            }
        }
        
        return dp[N][K];
    }

时间复杂度为O(KN^2)
好了。没过。


官方解答

求min(max(dp[n-i][k], dp[i-1][k-1]))时是对i遍历,找出最小的值,观察到dp[n-i][k]随i单调递减,dp[i-1][k-1]随i单调递增,最优的i值就是两条线的交点,可以用二分求解。

    int superEggDrop(int K, int N) {
        int dp[N+2][K+2];
        for (int i = 0; i <= N; i ++) {
            dp[i][0] = 0;
            dp[i][1] = i;
        }
        for (int i = 0; i <= K; i ++) {
        	dp[0][i] = 0;
        }
        
        for (int k = 2; k <= K; k ++) {
            for (int n = 1; n <= N; n ++) {
                int il = 1, ih = n;
                
                while (il+1 < ih) {
                	int mid = (il+ih)/2;

                	if (dp[n-mid][k] > dp[mid-1][k-1]) il = mid;
                	else if (dp[n-mid][k] < dp[mid-1][k-1]) ih = mid;
                	else il = ih = mid;
                }
                
                if (il == ih) dp[n][k] = max(dp[n-ih][k], dp[ih-1][k-1]) + 1;
                else {
                	int ansl = max(dp[n-il][k], dp[il-1][k-1]);
                    int ansh = max(dp[n-ih+1][k], dp[ih-2][k-1]);
                    if (ansl < ansh) dp[n][k] = ansl + 1;
                    else dp[n][k] = ansh + 1;
                }

            }
        }
        
        return dp[N][K];
    }

时间复杂度为O(KNlog(N))


其实,最优的 i 是随着 n 单调递增的。对每个k,只要保持最优的 i 随 s 单调增长就行。

	int superEggDrop(int K, int N) {
        int dp[N+2][K+2];
        for (int i = 0; i <= N; i ++) {
            dp[i][0] = 0;
            dp[i][1] = i;
        }
        for (int i = 0; i <= K; i ++) {
        	dp[0][i] = 0;
        }
        
        for (int k = 2; k <= K; k ++) {
        	int i = 1;
            for (int n = 1; n <= N; n ++) {

                while (i < n && dp[n-i][k] > dp[i-1][k-1]) i ++;

                dp[n][k] = max(dp[n-i][k], dp[i-1][k-1]) + 1;

            }
        }
        
        return dp[N][K];
    }

时间复杂度为O(KN)


还有一种特别绕,但特别简洁的算法,想出来它的人智商可能得两百多。

没看懂。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值