LeetCode 375. Guess Number Higher or Lower II

简介: 我们正在玩一个猜数游戏,游戏规则如下:我从 1 到 n 之间选择一个数字,你来猜我选了哪个数字。每次你猜错了,我都会告诉你,我选的数字比你的大了或者小了。然而,当你猜了数字 x 并且猜错了的时候,你需要支付金额为 x 的现金。直到你猜到我选的数字,你才算赢得了这个游戏。

v2-e767d70e5d09a90c5414a88104a5e666_1440w.jpg

Description



We are playing the Guess Game. The game is as follows:

I pick a number from 1 to n. You have to guess which number I picked.

Every time you guess wrong, I'll tell you whether the number I picked is higher or lower.


However, when you guess a particular number x, and you guess wrong, you pay $x. You win the game when you guess the number I picked.


Example:

n = 10, I pick 8.


First round: You guess 5, I tell you that it's higher. You pay $5.

Second round: You guess 7, I tell you that it's higher. You pay $7.

Third round: You guess 9, I tell you that it's lower. You pay $9.

Game over. 8 is the number I picked.

You end up paying 5 +5+7 + 9 =9=21.

Given a particular n ≥ 1, find out how much money you need to have to guarantee a win.


描述



我们正在玩一个猜数游戏,游戏规则如下:

我从 1 到 n 之间选择一个数字,你来猜我选了哪个数字。

每次你猜错了,我都会告诉你,我选的数字比你的大了或者小了。

然而,当你猜了数字 x 并且猜错了的时候,你需要支付金额为 x 的现金。直到你猜到我选的数字,你才算赢得了这个游戏。


示例:

n = 10, 我选择了8.


第一轮: 你猜我选择的数字是5,我会告诉你,我的数字更大一些,然后你需要支付5块。

第二轮: 你猜是7,我告诉你,我的数字更大一些,你支付7块。

第三轮: 你猜是9,我告诉你,我的数字更小一些,你支付9块。

游戏结束。8 就是我选的数字。

你最终要支付 5 + 7 + 9 = 21 块钱。

给定 n ≥ 1,计算你至少需要拥有多少现金才能确保你能赢得这个游戏。


思路


  • 题目中提到「当你猜了一个数之后,会告诉你,你猜的这个数和真实的数比,是大了还是小了」,但是此题目和 374 题目不同,并不会提供 guess 的 API,因为用不到。
  • 这道题和被猜的数无关,只和范围 n 有关,因此函数参数里只有 n;下面对题意进行详细的解释。
  • 假定 n 等于 10,需要猜的数为 g,那么 1. g 的范围是 [1,10](题目中明确从 1 开始)2.每次你猜的数也一定属于 [1,10]。
  • 假定 n 等于 10,需要猜的数为 1;那么猜中 1 有很多种方法;比如你一来就猜中了 1,不需要花钱;你猜 5,猜 3,猜2,猜 1,一共花了 10元;你猜 10,你猜 5,猜 3,猜 2,猜1,花了 20 元;在上面的 3 种猜法中,为了保证一定赢,你需要 20 元,记至少需要花的钱为 c1;
  • 假定 n 等于 10,需要猜的数为 2;猜中 2 有很多可能,如一来就猜 2 ,不需要花钱;猜 5,猜 3,猜 2,花费了 8 元;猜 10,猜 5,猜 3,猜 2,花费了 18元;在上面的 3 种猜法中,为了保证一定赢,你需要 18 元,记至少需要花的钱为 c2;
  • 同理可以获得 c3,c4 .... c10,题目要求的是 res = max(c1,c2...c10),也就是说在范围 n 内,无论被猜的数是几,使用 res 数量的钱一定可以把这个数猜出来。
  • 从上面的过程可以看到,不论被猜的数是几,我们都可以从 n 的中间值开始猜起,然后一路猜到被猜的数,这是一种猜的策略。
  • 有没有更好的猜的策略呢?有的。题目要求的是:在使用这种策略下,至少需要的钱数。以下以 n 为 10为例,说明一下此时得最优策略。如下图:


v2-614c50f6f3975e4ab87326cb62a6c005_720w.jpg


  • 当 n 为 10 时,无论被猜得数是什么,我们总是从 7 开始猜,下面给出每个一数得猜出路径;
  • 7-->3-->1,10;
  • 7-->3-->2,10;
  • 7-->3,7;
  • 7-->3-->5-->4,15;
  • 7-->3-->5,10;
  • 7-->3-->5-->6,15;
  • 7,0;
  • 7-->9-->8,16;
  • 7-->9,7;
  • 7-->9-->10,16;
  • 最后一个数表示需要花费的钱数,可以看出,当 n 为 10 时,最少需要 16 才能保证一定赢;
  • 根据题意,记被猜的数的集合为 t = [1,n]
  • 使用动态规划,dp[left][right] 表示当被猜的集合 t 为 [left,right] 时最少需要的钱数,dp[3][5] = 4 表示要猜中[3,5]中的所有数,最少需要 4 元。
  • 那么 dp[1][n] 表示要猜中 [1,n] 中的所有数最少需要的钱数,也就是题意所求。
  • 假定我们猜的范围为 n 到 n ,那么此时不需要花费任何的钱,因为候选集合只有 1 个数,直接猜就行;
  • 假定我们猜的范围为 n-1 到 n ,如 n = 10, 猜的为范围设置为[9,10],次数我们一定从小的数 9 开始猜,此时最少需要 9 元;
  • 假定我们猜的范围为 n-2 到 n ,如 n = 10, 猜的为范围设置为[8,9,10],次数我们一定 9 开始猜,此时最少需要 9 元;
  • ...
  • 为了猜中 t = [left,right] 中的所有数;我们可以尝试以 t 中的每一个数为第一个被猜的数,找到此时猜完所有数的最坏情况;每一个 x 对应一个最坏值,所有最坏值得最小值就是 dp[left][right] 的值。


# -*- coding: utf-8 -*-
# @Author:             何睿
# @Create Date:        2019-06-30 10:54:08
# @Last Modified by:   何睿
# @Last Modified time: 2019-06-30 14:56:28
class Solution(object):
    def getMoneyAmount(self, n):
        """
        :type n: int
        :rtype: int
        """
        dp = [[0] * (n + 1) for _ in range(n + 1)]
        for right in range(1, n + 1):
            for left in range(right - 1, 0, -1):
                # 第一个数猜 x,为了猜完 [left,right] 所有的数
                # 我们选择 max(dp[left][x - 1], dp[x + 1][right]),从而保证大的一边可以被猜完;
                # 大的一边可以被猜完,小的一边自然也可以被猜完;
                dp[left][right] = min(x + max(dp[left][x - 1], dp[x + 1][right]) for x in range(left, right))
        return dp[1][n]

源代码文件在 这里


目录
相关文章
|
算法
Leetcode 313. Super Ugly Number
题目翻译成中文是『超级丑数』,啥叫丑数?丑数就是素因子只有2,3,5的数,7 14 21不是丑数,因为他们都有7这个素数。 这里的超级丑数只是对丑数的一个扩展,超级丑数的素因子不再仅限于2 3 5,而是由题目给定一个素数数组。与朴素丑数算法相比,只是将素因子变了而已,解法还是和朴素丑数一致的。
104 1
|
jenkins 持续交付
项目采坑日志——cannot create a build with number 9 since that (or higher) is already in use among [12]
项目采坑日志——cannot create a build with number 9 since that (or higher) is already in use among [12]
174 0
|
6月前
|
存储 SQL 算法
LeetCode 题目 65:有效数字(Valid Number)【python】
LeetCode 题目 65:有效数字(Valid Number)【python】
|
7月前
|
存储 算法
【LeetCode力扣】单调栈解决Next Greater Number(下一个更大值)问题
【LeetCode力扣】单调栈解决Next Greater Number(下一个更大值)问题
53 0
|
存储
Leetcode Single Number II (面试题推荐)
给你一个整数数组,每个元素出现了三次,但只有一个元素出现了一次,让你找出这个数,要求线性的时间复杂度,不使用额外空间。
42 0
|
算法
LeetCode 414. Third Maximum Number
给定一个非空数组,返回此数组中第三大的数。如果不存在,则返回数组中最大的数。要求算法时间复杂度必须是O(n)。
97 0
LeetCode 414. Third Maximum Number
|
算法
LeetCode 405. Convert a Number to Hexadecimal
给定一个整数,编写一个算法将这个数转换为十六进制数。对于负整数,我们通常使用 补码运算 方法。
101 0
LeetCode 405. Convert a Number to Hexadecimal
|
API
LeetCode 374. Guess Number Higher or Lower
我们正在玩一个猜数字游戏。 游戏规则如下: 我从 1 到 n 选择一个数字。 你需要猜我选择了哪个数字。 每次你猜错了,我会告诉你这个数字是大了还是小了。
79 0
LeetCode 374. Guess Number Higher or Lower
LeetCode contest 190 5417. 定长子串中元音的最大数目 Maximum Number of Vowels in a Substring of Given Length
LeetCode contest 190 5417. 定长子串中元音的最大数目 Maximum Number of Vowels in a Substring of Given Length
LeetCode Contest 178-1365. 有多少小于当前数字的数字 How Many Numbers Are Smaller Than the Current Number
LeetCode Contest 178-1365. 有多少小于当前数字的数字 How Many Numbers Are Smaller Than the Current Number