好久没切 leetcode 的题了,静下心来切了道,这道题比较有意思,和大家分享下。

我把它叫做 "不一样的猜数字游戏",我们先来看看传统的猜数字游戏,Guess Number Higher or Lower。题意非常的简单,给定一个数字 n,系统会随机从 1 到 n 中抽取一个数字,你需要写一个函数 guessNumber,它的作用是返回系统选择的数字,同时你还有一个外部的 API 可以调用,是为 guess 函数,它会将你猜的数字和系统选择的数字比较,是大了还是小了。

非常的简单,稍微有点常识的童鞋应该都能想到二分查找的方案(插句题外话,这游戏让我想到了儿时的幸运52)。关于二分查找,可以参考下我以前写的一篇文章 http://www.cnblogs.com/zichi/p/5118032.html,几乎囊获了所有二分查找的情况。这道题代码比较简单,可以参考 guess-number-higher-or-lower.cpp,比较蛋疼的是不支持 JavaScript。

核心代码:

int guessNumber(int n) {
  int start = 1, end = n;
  int ans;

  while (start <= end) {
    int mid = start + (end - start) / 2;
    int val = guess(mid);

    if (val == -1)
      end = mid - 1;
    else if (val == 1)
      start = mid + 1;
    else {
      ans = mid;
      break;
    }
  }

  return ans;
}

还有一点需要注意下,取 mid 值时不能用 (start + end) / 2,不然会溢出,TLE 掉!

接着进入正题,来看 Guess Number Higher or Lower II 这道题,跟前者比,有何区别呢?同样是给定一个数字 n,系统会随机从 1 到 n 中选择一个整数,你要做的还是将这个数猜出来。你每猜一个数字,需要花费一定的 money,比如你猜 m,那么你就要花费 m,求解你要将这个数字猜出来,至少需要的 money

举个栗子,比如 n 为 5,系统选择的数字是 1。如果我先猜 3,系统提示你猜大了,然后再猜 2,系统提示你猜大了,那么你就可以确定是 1 了,花费 3+2=5。但是很明显第二次猜测应该猜 1,这样花费就少了。再比如我选的是 4,第一次还是猜 3,系统提示你猜小了,第二次猜 4,中了,总共花费 3+4=7,如果 n 为 5,至少需要 7?非也,正确的解法是先猜 4,如果数字在 1-3 之间,那么再猜 2,至少需要的应该是 6!

这是一道很典型的动态规划题,你根本不可能去盲目地猜,然后使劲地暴力递归去解!这样的复杂度是指数级的。是否能够递推求解?比如已经知道 n 为 1-5 的情况,当 n 为 6 时,第一次猜,我们可以有 6 种猜法,分别选择 1,2,3,4,5 和 6,我们以猜 3 为例,比如说第一把猜了 3,那么如果猜的大了,那么我们接下去要求的是从 [1, 2] 中猜到正确数字所需要花费的最少 money,记为 x,如果猜的小了,那么我们接下去要求的是从 [4, 6] 中猜到正确数字所需要花费的最少 money,记为 y,如果刚好猜中,则结束。很显然,如果第一把猜 3,那么猜中数字至少需要花费的 money 为 3 + max(x, y, 0),"至少需要的花费",就要我们 "做最坏的打算,尽最大的努力",即取最大值。这是第一把取 3 的情况,我们还需要考虑其他 5 种情况,然后六种情况再取个最小值,就是 n=6 至少需要的 money!(想想,是不是这样?)

最后来编码,我们需要一个二维数组来表示最值。首先我们定义一个二维数组 ans[][],ans[i][j] 表示 i-j 中任取一个数字,猜中这个数字需要至少花费的 money。

定义 ans 数组,并且初始化:

// ans[i][j] 表示从 [i, j] 中任取一个数字
// 猜中这个数字至少需要花费的 money
var ans = [];
for (var i = 0; i <= n; i++)
  ans[i] = [];

接着我们定义一个函数 DP,DP(ans, x, y) 表示 [x, y] 中任取一个数字,猜中这个数字需要花费的最少 money,而 ans 是为数组的引用。很显然,我们要求的就是 DP(ans, 1, n) 的返回值,直接看代码。

function DP(ans, from, to) {
  // 如果 from >= to
  if (from >= to)
    return 0;

  // 如果 ans[from][to] 已经求得
  // 直接 return
  if (ans[from][to])
    return ans[from][to];

  // 先赋值 Infinity,便于之后的比较
  ans[from][to] = Infinity;

  // 现在要从 [from, to] 中猜数字
  // 假设先猜 i,i 可以是 [from, to] 中的任何数字,遍历之
  for (var i = from; i <= to; i++) {
    // left 为从 [from, i - 1] 猜对数字至少需要花费的 money
    var left = DP(ans, from, i - 1);
    // right 为从 [i + 1, to] 猜对数字至少需要花费的 money
    var right = DP(ans, i + 1, to);

    // tmp 为先猜 i,从 [from, to] 猜对数字至少需要花费的 money
    var tmp = i + Math.max(left, right);

    // 跟别的方案比较(即跟不是先猜 i 的方法比较)
    // 取最小值
    ans[from][to] = Math.min(ans[from][to], tmp);
  }

  return ans[from][to];
}

注释写的很清晰了,如果再细分的话,个人觉得这可以说是一道 "记忆化DP",不晓得有没有这个词?好像只听说过 "记忆化搜索"?DP 本来就是记忆化的过程吧?好了不钻牛角尖了,完整代码可以从我们的 Repo https://github.com/hanzichi/leetcode 获取。

不一样的猜数字游戏 — leetcode 375. Guess Number Higher or Lower II的更多相关文章

  1. [LeetCode] 375. Guess Number Higher or Lower II 猜数字大小 II

    We are playing the Guess Game. The game is as follows: I pick a number from 1 to n. You have to gues ...

  2. [LeetCode] 375. Guess Number Higher or Lower II 猜数字大小之二

    We are playing the Guess Game. The game is as follows: I pick a number from 1 to n. You have to gues ...

  3. [leetcode]375 Guess Number Higher or Lower II (Medium)

    原题 思路: miniMax+DP dp[i][j]保存在i到j范围内,猜中这个数字需要花费的最少 money. "至少需要的花费",就要我们 "做最坏的打算,尽最大的努 ...

  4. Leetcode 375. Guess Number Higher or Lower II

    We are playing the Guess Game. The game is as follows: I pick a number from 1 to n. You have to gues ...

  5. Leetcode之二分法专题-374. 猜数字大小(374. Guess Number Higher or Lower)

    Leetcode之二分法专题-374. 猜数字大小(374. Guess Number Higher or Lower) 我们正在玩一个猜数字游戏. 游戏规则如下:我从 1 到 n 选择一个数字. 你 ...

  6. 【LeetCode】375. Guess Number Higher or Lower II 解题报告(Python)

    [LeetCode]375. Guess Number Higher or Lower II 解题报告(Python) 作者: 负雪明烛 id: fuxuemingzhu 个人博客: http://f ...

  7. 375 Guess Number Higher or Lower II 猜数字大小 II

    我们正在玩一个猜数游戏,游戏规则如下:我从 1 到 n 之间选择一个数字,你来猜我选了哪个数字.每次你猜错了,我都会告诉你,我选的数字比你的大了或者小了.然而,当你猜了数字 x 并且猜错了的时候,你需 ...

  8. leetcode 374. Guess Number Higher or Lower 、375. Guess Number Higher or Lower II

    374. Guess Number Higher or Lower 二分查找就好 // Forward declaration of guess API. // @param num, your gu ...

  9. 375. Guess Number Higher or Lower II

    最后更新 四刷? 极大极小算法..还是叫极小极大的.. 首先要看怎么能保证赢. 比如2个数,猜第一个猜第二个都能保证下一轮我们赢定了,为了少交钱,我们猜小的. 比如3个数,猜第二个才能保证下一轮再猜一 ...

随机推荐

  1. JavaScript时间处理之几个月前或几个月后的指定日期

    在平常项目开发过程中,经常会遇到需要在JavaScript中处理时间的情况,无非两种(1,逻辑处理  2,格式转换处理).当然要说相关技术博,园子里闭着眼睛都能抓一把,但是我要做的是:既然有幸被我碰到 ...

  2. C#图片处理常见方法性能比较

    C#图片处理常见方法性能比较 来自:http://www.cnblogs.com/sndnnlfhvk/archive/2012/02/27/2370643.html   在.NET编程中,由于GDI ...

  3. Sql 获取日期区间

    获取制定日期区间 declare @d as date declare @d2 as date set @d = '2014-06-03' set @d2 ='2014-06-10' ),datead ...

  4. dede:field name='imgurls'不能二次使用的解决办法

    {dede:field name='imgurls' alt='图片输出区'}图片链接  [field:linkurl/]图片地址 [field:imgsrc/]{/dede:field} 这个标签不 ...

  5. IOS开发基础知识--碎片10

    1:如何给表格单元列增加选择时的背影效果 if (cell == nil) { cell = [[UITableViewCell alloc] initWithStyle:UITableViewCel ...

  6. 优质Android小部件:索尼滚动相册

    虽然骚尼手机卖的不怎么样,但是有些东西还是做的挺好的,工业设计就不用说了,索尼的相册的双指任意缩放功能也是尤其炫酷.其桌面小部件滚动相册我觉得也挺好的,比谷歌原生的相册墙功能好多了,网上搜了一下也没发 ...

  7. APP上架证书无效:解决

    转发:http://www.cnblogs.com/pruple/p/5523767.html 转发:http://blog.csdn.net/sunnyboy9/article/details/50 ...

  8. 使用putty与SSHSecureShellClient登录远程服务器完成与本地Git项目的同步

    使用软件远程登录管理服务器 今天给大家介绍两款远程登录管理服务器的软件(Putty和SSHSecureShellClient),这两款也是我在工作中经常的软件. 使用 PuTTY 远程登录管理服务器 ...

  9. C语言数组实现约瑟夫环问题,以及对其进行时间复杂度分析

    尝试表达 本人试着去表达约瑟夫环问题:一群人围成一个圈,作这样的一个游戏,选定一个人作起点以及数数的方向,这个人先数1,到下一个人数2,直到数到游戏规则约定那个数的人,比如是3,数到3的那个人就离开这 ...

  10. msdia80.dll文件出现在磁盘根目录下的解决方案

    情况描述: 当安装某些软件后,磁盘根目录中多出了msdia80.dll文件,该文件显示为2006年12月1日,884KB. 原因:   当使用64位操作系统时,在电脑上安装 Microsoft Vis ...