LeetCode887鸡蛋掉落——dp

题目

题目链接

你将获得 K 个鸡蛋,并可以使用一栋从 1 到 N  共有 N 层楼的建筑。
每个蛋的功能都是一样的,如果一个蛋碎了,你就不能再把它掉下去,如果没有碎可以继续使用。
你知道存在楼层 F ,满足 0 <= F <= N 任何从高于 F 的楼层落下的鸡蛋都会碎,从 F 楼层或比它低的楼层落下的鸡蛋都不会破。
每次移动,你可以取一个鸡蛋(如果你有完整的鸡蛋)并把它从任一楼层 X 扔下(满足 1 <= X <= N)。
你的目标是确切地知道 F 的值是多少。
无论 F 的初始值如何,请你确定 F 的值的最小移动次数是多少?

例如:

输入:K = 1, N = 2
输出:2
输入:K = 2, N = 100
输出:14
输入:K = 3, N = 14
输出:4

注意

  • 1 <= K <= 100
  • 1 <= N <= 10000

思路

算法一

(动态规划)$O(K\!N)$

  1. 状态$f(i,j)$表示使用$i$个鸡蛋,最大楼层为$j$时,所需要的最小移动次数。
  2. 显然初始状态$f(1,j)=j$。对于状态$f(i,j)$,如果枚举上一次测试为楼层$s$,则可以得到如下转移$f(i,j) = min_{s=1}^j(max(f(i-1,s-1),f(i,j-s)) + 1)$,分别表示在第$s$层摔碎和没摔碎。
  3. 如果直接枚举$s$,则总的时间复杂度为$O(K\!N^2)$,无法通过。考虑$f(i-1,s-1)和f(i,j-s)$的大小关系,可以发现,前者随着$s$单调递增,后者单调递减,且每次变化的值最多为1(可证明,略)。所以,如果存在${s}‘$使得$f(i-1,s-1) = f(i,j-s)$,则此时${s}‘$就是最优的;否则取两者最相近的两个$s$作比较,取最小值。
  4. 至此,$s$可以二分解决;总的时间复杂度:$O(K\!N\!logN)$。
  5. 但进一步可以发现,$s$会随着$j$的增加而增加,即最优决策点${s}‘$是随着$j$单调递增的。因为对于固定的$s$,$f(i,j-s)$会随着$j$而增加,这就会造成3中的最优决策点也会向后移动。所以,我们只需在每次移动$j$后,继续从上次的${s}‘$向后寻找最优决策点即可。
  6. 最终答案是$f(K,N)$

注意:在鸡蛋没摔碎时,我们还能用这$i$个鸡蛋在在上面的$j-s$层确定$F$,这里的实验与在第$1~(j-w)$所需的次数是一样的,因为它们的实验方法和步骤都是相同的,只不过这$(j-w)$层在上面罢了。

时间复杂度

  • 状态数为$O(K\!N)$,对于每个$i$,寻找最优决策的均摊时间为$O(N)$,故总时间复杂度为$O(K\!N)$

C++代码

 1 class Solution {
 2 public:
 3     int superEggDrop(int K, int N) {
 4         int m = K, n = N;
 5         const int maxn = 100 + 10;
 6         const int maxm = 10000 + 10;
 7         int d[maxn][maxm];        //dp[i][j]表示有i颗鹰蛋在j层楼的最少次数
 8         if (m >= ceil(log(n + 1) * 1.0) / log(2.0))
 9             return (int)ceil(log((n + 1) * 1.0) / log(2.0));
10         else
11         {
12             memset(d, 0, sizeof(d));
13             for (int i = 1; i <= n; i++)  d[1][i] = i;
14             for (int i = 2; i <= m; i++)
15             {
16                 int s = 1;
17                 for (int j = 1; j <= n; j++)
18                 {
19                     d[i][j] = d[i][j - 1] + 1;        //
20
21                     while (s < j && d[i - 1][s] < d[i][j - s - 1])  s++;
22
23                     d[i][j] = min(d[i][j], max(d[i - 1][s - 1], d[i][j - s]) + 1);
24                     if (s < j)  d[i][j] = min(d[i][j], max(d[i - 1][s], d[i][j - s - 1]) + 1);
25                 }
26             }
27             return d[m][n];
28         }
29     }
30 };

算法二

(动态规划)$O(KlogN)$

  1. 状态$f(i,j)$表示进行$i$次移动,有$j$个鸡蛋,最多可以检查的楼层高度是多少。
  2. 初始状态是$f(1,0)=0$,$f(1,j),j \geq 1$。
  3. 先给出转移方程,$f(i,j)= f(i-1,j-1)+f(i-1,j)+1$。假设$n_1=f(i-1,j-1),n_2=f(i-1,j)$,我们在第$i$次移动时测试第$n_1+1$层。
  4. 如果测试时鸡蛋碎掉了,则我们可以通过$i-1$次移动和$j-1$个鸡蛋来找到最高不会碎掉的楼层,因为楼层不会超过$n_1$了;如果鸡蛋没有碎掉,则在此基础上,我们可以使用$i-1$次移动和$j$个鸡蛋,在继续向上检查$n_2$层,故答案在$\left [ 0, n_1 + n_2 + 1 \right ]$范围内,都可以通过$i$次移动、$j$个鸡蛋来找到。
  5. 返回最小的$m$满足,$f(m,K) \geq N$。
  6. 这里第一维可以省略,更新时只需要倒序更新即可。

时间复杂度

  • 最多进行$logN$轮更新,每轮更新需要$O(K)$的时间,故时间复杂度$O(K\!logN)$。

C++代码

 1 class Solution {
 2 public:
 3     int superEggDrop(int K, int N) {
 4         vector<int> f(K + 1, 1);
 5         f[0] = 0;
 6         int m = 1;
 7         while (f[K] < N) {
 8             for (int i = K; i >= 1; i--)
 9                 f[i] = f[i] + f[i - 1] + 1;
10             m++;
11         }
12
13         return m;
14     }
15 };

在URAL OJ上也有这题,只是数据范围有所不同:Chernobyl’ Eagle on a Roof

参考链接:

1、https://www.acwing.com/solution/leetcode/content/579/

2、朱晨光:《优化,再优化!——从《鹰蛋》一题浅析对动态规划算法的优化

原文地址:https://www.cnblogs.com/lfri/p/10350092.html

时间: 2024-08-28 13:06:35

LeetCode887鸡蛋掉落——dp的相关文章

力扣第887题 鸡蛋掉落

力扣第887题 鸡蛋掉落 class Solution { public: int superEggDrop(int K, int N) { vector<vector<int>> dp(K+1, vector<int>(N+1, 0)); int m = 0; for (; dp[K][m] < N;) { m++; for (int k = 1; k <= K; k++) dp[k][m] = 1 + dp[k-1][m-1] + dp[k][m-1];

动态规划法(六)鸡蛋掉落问题(二)

??上次我们讲到,我们的主人公丁丁由于用动态规划法解决了鸡蛋掉落问题(egg dropping problem)而获得了当地科学家的赏识.这不,正当丁丁还沉浸在解决问题的喜悦中,科学家又给丁丁出了一个难题: 假设有n个鸡蛋和d次尝试机会,那么,最多能探索多少层楼? 这无疑是鸡蛋问题的翻版,因为这两个问题实在太像了.丁丁没有犹豫,立马按照之前的想法开始思考: ??用\(f(d, n)\)表示该问题的解.假设从k层楼扔下鸡蛋(k足够大),若鸡蛋碎了,则剩下n-1个鸡蛋,d-1次尝试机会,最多能向下探

鸡蛋掉落

题目:鸡蛋掉落 问题描述: 你将获得 K 个鸡蛋,并可以使用一栋从 1 到 N 共有 N 层楼的建筑. 每个蛋的功能都是一样的,如果一个蛋碎了,你就不能再把它掉下去. 你知道存在楼层 F ,满足 0 <= F <= N 任何从高于 F 的楼层落下的鸡蛋都会碎,从 F 楼层或比它低的楼层落下的鸡蛋都不会破. 每次移动,你可以取一个鸡蛋(如果你有完整的鸡蛋)并把它从任一楼层 X 扔下(满足 1 <= X <= N). 你的目标是确切地知道 F 的值是多少. 无论 F 的初始值如何,你确

POJ 3783 Balls(扔鸡蛋问题——DP动态规划)

传送门 Balls Time Limit: 1000MS Memory Limit: 65536K Total Submissions: 892 Accepted: 588 Description The classic Two Glass Balls brain-teaser is often posed as: "Given two identical glass spheres, you would like to determine the lowest floor in a 100-s

[LeetCode] 887. 鸡蛋掉落

这道题好变态...... import java.util.Arrays; public class Solution { public int superEggDrop(int K, int N) { // dp[i][j]:一共有 i 层楼梯的情况下,使用 j 个鸡蛋的最少仍的次数 int[][] dp = new int[N + 1][K + 1]; // 初始化 for (int i = 0; i <= N; i++) { Arrays.fill(dp[i], i); } for (in

leet

# 题名1 两数之和    2 两数相加    3 无重复字符的最长子串    4 寻找两个有序数组的中位数    5 最长回文子串    6 Z 字形变换    7 整数反转    8 字符串转换整数 (atoi)    9 回文数    10 正则表达式匹配    11 盛最多水的容器    12 整数转罗马数字    13 罗马数字转整数    14 最长公共前缀    15 三数之和    16 最接近的三数之和    17 电话号码的字母组合    18 四数之和    19 删除链表

小年糕秋招面试复盘

10月18日笔试 一共三道算法题,都在剑指offer上有类似的题,一点小缺陷是约瑟夫问题递推公式当时没有想起来,只能用暴力解的. 1.一道排序 2.约瑟夫问题 3.动态规划上楼梯 10月29日现场面试 先花了半个小时针对简历上的东西进行提问,又用了半个小时做手撕4道算法题.   1.项目相关,django框架 2.机器学习模型有几层,怎么训练的 3.python排序的方法 4.python对dict的key排序 5.python多态 6.python多继承概念 7.sql join关键字 8.m

poj 3783 DP 2个鸡蛋扔100层楼的加强版

http://poj.org/problem?id=3783 估计23号之后的排位赛之后我就要退役了,这之前最后再做5天ACM 今天的排位很惨,上次排位也很惨......这道题原来算法课老师讲过,模模糊糊记得方程,但是边界处理有问题, dp[i][j]=min(1+max(dp[k-1][j-1],dp[i-k][j]))   k=1 to 楼数 dp[i][j]:i层楼扔,手里有j个ball 的次数 边界两个:1.dp[1][i]=1,第一层无论手里有几个鸡蛋都是1次,2.dp[i][1]=i

鸡蛋的硬度

描述 最近XX公司举办了一个奇怪的比赛:鸡蛋硬度之王争霸赛.参赛者是来自世 界各地的母鸡,比赛的内容是看谁下的蛋最硬,更奇怪的是XX公司并不使用什么精密仪器来测量蛋的硬度,他们采用了一种最老土的办法--从高度扔鸡蛋--来 测试鸡蛋的硬度,如果一次母鸡下的蛋从高楼的第a层摔下来没摔破,但是从a+1层摔下来时摔破了,那么就说这只母鸡的鸡蛋的硬度是a.你当然可以找出各种 理由说明这种方法不科学,比如同一只母鸡下的蛋硬度可能不一样等等,但是这不影响XX公司的争霸赛,因为他们只是为了吸引大家的眼球,一个个