好久没切 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. VS中如何快捷地给自己的代码添加创建信息注释

    VS中如何快捷地给自己的代码添加创建信息注释 Intro 以下讨论的都是没有使用 GIT 来管理源代码的情况,如果使用 GIT 管理源代码可直接使用VS的Git扩展就不需要考虑以下问题. 什么是创建信 ...

  2. Atitit.加密算法ati Aes的框架设计

    Atitit.加密算法ati Aes的框架设计 版本进化 c:\1t\aesC47.java c:\1t\aes.java 增加了public static byte[] encrypt(byte[] ...

  3. plist的读取和写入

    // 从plist中读取数组数据 let arrPath = Bundle.main.path(forResource: "ArrayPList", ofType: "p ...

  4. Android+PHP+MYSQL把数据库中的数据显示在Android界面上

    俗话说,好记性不如烂笔头.今天终于体会其中的道理了.昨天写好的代码不知道为何找不到了.所以今天我一定得抽出一点时间把我的代码保存起来,以防我的代码再没有了. 还是先上图片. 这个界面是用ListVie ...

  5. 【转】JavaScript中的原型和继承

    请在此暂时忘记之前学到的面向对象的一切知识.这里只需要考虑赛车的情况.是的,就是赛车. 最近我正在观看 24 Hours of Le Mans ,这是法国流行的一项赛事.最快的车被称为 Le Mans ...

  6. TortoiseSVN和VisualSVN-Server的配置使用,外网访问SVN版本库

    TortoiseSVN和VisualSVN-Server的配置使用,外网访问SVN版本库 SVN客户端程序:TortoiseSVN SVN服务器程序:VisualSVN-Server ######## ...

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

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

  8. Ant :DataType

    DataType patternset fileset selector filelist path regexp Ant datatype Ant中,除了Property可以做为Task执行时使用的 ...

  9. WINDOWS登录系统之前(欢迎界面)运行指定程序脚本服务

    方法一:创建可在系统登录之前运行的服务 PS:需要用到两个程序—Srvany和Instsrv,点击 http://pan.baidu.com/share/link?shareid=4111024491 ...

  10. 【hive】——Hive初始了解

    1.没有接触,不知道这个事物是什么,所以不会产生任何问题.2.接触了,但是不知道他是什么,反正我每天都在用.3.有一定的了解,不够透彻.那么hive,1.我们对它了解多少?2.它到底是什么?3.hiv ...