算法—leetcode—887. 鸡蛋掉落

597 阅读1分钟

题目

  1. 鸡蛋掉落

题目描述

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

案例

示例一

输入:K = 1, N = 2
输出:2
解释:
鸡蛋从 1 楼掉落。如果它碎了,我们肯定知道 F = 0 。
否则,鸡蛋从 2 楼掉落。如果它碎了,我们肯定知道 F = 1 。
如果它没碎,那么我们肯定知道 F = 2 。
因此,在最坏的情况下我们需要移动2次以确定F是多少。

示例二

输入:K = 2, N = 6
输出:3

示例三

输入:K = 3, N = 14
输出:4

提示

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

思路

已知条件:K个鸡蛋,N层楼

有什么状态?
就是发生变化的量,已知存在两个,当前拥有的鸡蛋数K,需要测试的楼层数N,随着测试的进行,鸡蛋个数会减少, 楼层个数可能减少,这就是状态的变化。

有什么选择?
去哪层楼扔鸡蛋

状态转移
如果选择第i层扔鸡蛋
如果鸡蛋碎了 K->K-1, 0 ~ N->0 ~ i-1
如果鸡蛋没碎 K->K, 0~N -> i+1 ~ N

代码

package leetcode

import (
	"fmt"
)

// superEggDrop
// 887. 鸡蛋掉落
func superEggDrop(K int, N int) int {
	// 备忘录解决重叠子问题
	meno := map[string]int{}

	var dp func(K, N int) int
	dp = func(K, N int) int {
		// base case
		// 楼层为0
		if N == 0 {
			return 0
		}
		// 只有一个鸡蛋
		if K == 1 {
			return N
		}

		// 校验备忘录中是否存在
		key := fmt.Sprintf("%d%d", K, N)
		if rs, ok := meno[key]; ok {
			// log.Println(rs)
			return rs
		}

		res := 1<<31 - 1
		for i := 1; i <= N; i++ {
			// 最坏情况
			// 鸡蛋没碎:K->K, 0~N -> i+1 ~ N
			// 鸡蛋碎了:K->K-1, 0~N -> 0 ~i-1
			maxNums := max(dp(K, N-i), dp(K-1, i-1)) + 1
			// 最少扔鸡蛋次数
			res = min(res, maxNums)
		}
		meno[key] = res
		return res
	}
	return dp(K, N)
}

func max(a, b int) int {
	if a > b {
		return a
	}
	return b
}

func min(a, b int) int {
	if a > b {
		return b
	}
	return a
}

// superEggDrop
// 887. 鸡蛋掉落
// 使用二分法提升效率
func superEggDropV2(K int, N int) int {
	// 备忘录解决重叠子问题
	meno := map[string]int{}

	var dp func(K, N int) int
	dp = func(K, N int) int {
		// base case
		// 楼层为0
		if N == 0 {
			return 0
		}
		// 只有一个鸡蛋
		if K == 1 {
			return N
		}

		// 校验备忘录中是否存在
		key := fmt.Sprintf("%d%d", K, N)
		if rs, ok := meno[key]; ok {
			return rs
		}

		res := 1<<31 - 1
		left, right := 1, N
		for left <= right {
			mid := (left + right) / 2
			// 碎了
			broken := dp(K-1, mid-1)
			// 没碎
			notBroken := dp(K, N-mid)

			if broken > notBroken {
				right = mid - 1
				res = min(res, broken+1)
			} else {
				left = mid + 1
				res = min(res, notBroken+1)
			}
		}
		meno[key] = res
		return res
	}
	return dp(K, N)
}

参考

来源:力扣(LeetCode)
链接:leetcode-cn.com/problems/su…
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

[算法小抄@付东来]