【CODE】扔鸡蛋问题

887. Super Egg Drop

难度困难234收藏分享切换为中文关注反馈

You are given K eggs, and you have access to a building with N floors from 1 to N

Each egg is identical in function, and if an egg breaks, you cannot drop it again.

You know that there exists a floor F with 0 <= F <= N such that any egg dropped at a floor higher than F will break, and any egg dropped at or below floor F will not break.

Each move, you may take an egg (if you have an unbroken one) and drop it from any floor X (with 1 <= X <= N). 

Your goal is to know with certainty what the value of F is.

What is the minimum number of moves that you need to know with certainty what F is, regardless of the initial value of F?

Example 1:

Input: K = 1, N = 2
Output: 2
Explanation: 
Drop the egg from floor 1.  If it breaks, we know with certainty that F = 0.
Otherwise, drop the egg from floor 2.  If it breaks, we know with certainty that F = 1.
If it didn't break, then we know with certainty F = 2.
Hence, we needed 2 moves in the worst case to know what F is with certainty.

Example 2:

Input: K = 2, N = 6
Output: 3

Example 3:

Input: K = 3, N = 14
Output: 4

Note:

  1. 1 <= K <= 100
  2. 1 <= N <= 10000
#include"pch.h"
#include<iostream>
#include<vector>
#include<algorithm>
#include<string>
using namespace std;
/*1.超时,因为枚举了1到j内的所有k,时间复杂度为K*N^2*/
int superEggDrop1(int K, int N) {
	vector<vector<int> > dp(K + 1, vector<int>(N + 1));
	for (int j = 1; j <= N; j++) dp[1][j] = j;
	for (int i = 2; i <= K; i++) {
		for (int j = 1; j <= N; j++) {
			dp[i][j] = j;
			for (int k = 1; k < j; k++) {
				dp[i][j] = min(dp[i][j], max(dp[i - 1][k - 1], dp[i][j - k]) + 1);
			}
		}
	}
	return dp[K][N];
}
/*2.观察dp[i - 1][k - 1]和dp[i][j - k],前者k越大,值越大,后者k越大,值越小,
且每次变化的值最多为1,所以只要存在某个k使两者相等,那么就能找到最优解,
否则取对相近的两个k比较,所以可以在1到j范围内二分查找k,找到第一个使dp[i-1][k-1]大于等于dp[i][j-k]的k值,
然后用这个k去更新dp[i][j],复杂度减小到K*NlogN
执行用时 :756 ms, 在所有 C++ 提交中击败了5.45%的用户
内存消耗 :29.5 MB, 在所有 C++ 提交中击败了50.00%的用户*/
int superEggDrop2(int K, int N) {
	vector<vector<int> > dp(K + 1, vector<int>(N + 1));
	for (int j = 1; j <= N; j++) dp[1][j] = j;
	for (int i = 2; i <= K; i++) {
		for (int j = 1; j <= N; j++) {
			dp[i][j] = j;
			int left = 1, right = j;
			while (left < right) {
				int mid = left + (right - left) / 2;
				if (dp[i - 1][mid - 1] < dp[i][j - mid]) left = mid + 1;
				else right = mid;
			}
			dp[i][j] = min(dp[i][j], max(dp[i - 1][right - 1], dp[i][j - right]) + 1);
		}
	}
	return dp[K][N];
}
/*3.对于固定的k,dp[i][j-k]会随着j增加而增加,最优决策点也会增加,所以每次移动j后,
从上一次的最优决策点的位置来继续向后查找最优点即可,时间复杂度为KN。
使用s表示当前j值下的最优决策点,当j改变了,使用while循环,来找到下一个最优决策点s,使dp[i-1][s-1]大于等于dp[i][j-s]
执行用时 :180 ms, 在所有 C++ 提交中击败了15.88%的用户
内存消耗 :29.6 MB, 在所有 C++ 提交中击败了50.00%的用户*/
int superEggDrop3(int K, int N) {
	vector<vector<int> > dp(K + 1, vector<int>(N + 1));
	for (int j = 1; j <= N; j++) dp[1][j] = j;
	for (int i = 2; i <= K; i++) {
		int s = 1;
		for (int j = 1; j <= N; j++) {
			dp[i][j] = j;
			while (s < j && dp[i - 1][s - 1] < dp[i][j - s]) s++;
			dp[i][j] = min(dp[i][j], max(dp[i - 1][s - 1], dp[i][j - s]) + 1);
		}
	}
	return dp[K][N];
}
/*4.如果dp[i][j]表示有i次操作,j个鸡蛋能测出的最高楼层数,
dp[i][j]表示使用第i次操作且使用第j个鸡蛋测试第dp[i-1][j-1]+1层,
因为上一次操作是第i-1次移动,第j-1个鸡蛋,所以还是有两种情况:
(1)鸡蛋碎了:说明至少可以测到的不会碎的层数就是dp[i-1][j-1]
(2)鸡蛋没碎:那这个鸡蛋可以继续使用,此时还可以向上继续查找dp[i-1][j]层
那么加上当前层,总共可以通过i次操作和j个鸡蛋查找的层数范围是[0,dp[i-1][j-1]+dp[i-1][j]+1],
所以dp[i][j] = dp[i - 1][j - 1] + dp[i - 1][j] + 1,当dp[i][K]恰好小于N时,就是求得的最小次数
执行用时 :160 ms, 在所有 C++ 提交中击败了17.48%的用户
内存消耗 :47.4 MB, 在所有 C++ 提交中击败了28.57%的用户*/
int superEggDrop4(int K, int N) {
	vector<vector<int>> dp(N + 1, vector<int>(K + 1));
	int m = 0;
	while (dp[m][K] < N) {
		++m;
		for (int j = 1; j <= K; ++j) {
			dp[m][j] = dp[m - 1][j - 1] + dp[m - 1][j] + 1;
		}
	}
	return m;
}
/*5.进一步优化空间,因为当前操作次数的更新只与上一次有关,所以并不需要保存所有的次数,可以使用一个一维数组,
dp[i]表示当前次数下使用i个鸡蛋可以测出的最高楼层
执行用时 :4 ms, 在所有 C++ 提交中击败了86.18%的用户
内存消耗 :6.2 MB, 在所有 C++ 提交中击败了100.00%的用户*/
int superEggDrop5(int K, int N) {
	vector<int> dp(K + 1);
	int m = 0;
	for (; dp[K] < N; ++m) {
		for (int i = K; i > 0; i--) {
			dp[i] = dp[i] + dp[i - 1] + 1;
		}
	}
	return m;
}
int main() {
	return 0;
}

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值