一亩三分地论坛

 找回密码
 获取更多干货,去instant注册!

扫码关注一亩三分地公众号
查看: 3202|回复: 22
收起左侧

[算法题] Google的“+-游戏”最优策略下的O(N^2) DP解法

[复制链接] |试试Instant~ |关注本帖
stellari 发表于 2015-7-13 08:19:54 | 显示全部楼层 |阅读模式

注册一亩三分地论坛,查看更多干货!

您需要 登录 才可以下载或查看,没有帐号?获取更多干货,去instant注册!

x
本帖最后由 stellari 于 2015-7-13 13:09 编辑

原题:


给定一个仅由+和-组成的字符串,如“++++-----+++++-----++-+”,两名玩家轮流地将“相邻的两个++flip为--”,直至轮到一方时,如果他不能继续flip,则这名玩家输。问对于给定的字符串,如何判断(在双方都使用最优策略的情况下)先手玩家是否一定能赢?


此题的面经请点这里


------------------------------


这种解法需要了解博弈论的知识,一般面试应该是不做要求的。只是放在这里供大家参考:

首先,这种游戏属于“无偏博弈”(impartial game),意思是:“棋盘”上的棋子,即"+"号,双方都可以公平地使用;而且胜利的最终判定方法对二者来说是一样的。而象棋就不是无偏博弈,因为双方各有不同的棋子和胜利条件。其次这种游戏的终结条件是“正常终结条件”的,即最后无法move的那个人输。与之相对的还有一种叫做“misere终结条件”,即无法move的人赢。

以下将要谈到的所有定理和推理过程,都是仅适用于“无偏博弈”和“正常终结条件”的情况。

------------------------------

因为我们只能把连着的两个+号变为-号,所以对我们来说,“棋盘”状态可以由一组“连续的+的个数来表示”,比如“+++----+-++----++++”可以表示为(3, 1, 2, 4)。 因为其中那个单独的+号对结果没有影响,而且3和2出现的顺序也无关紧要,所以我们扔掉“1”,然后将这个序列排序,即得较简的表达方式:(2,3,4)。

所以,这个游戏其实就是从某个初始状态开始,不断进入下一个状态,直至无法改变状态为止。如果以每一个可能的状态做为一个节点,那么整个游戏中能出现的所有状态和这些状态直接的转移方式共同构成了一个有向无环图。之所以无环,是因为我们没法将-号变回+号,所以我们永远不可能回到之前的状态。

比如,从状态(2, 3, 4)开始,我们能进入的可能状态是
(2-2, 3, 4) => (3,4)
(2, 3-1, 4) => (2,4)
(2, 3, 4-2) => (2,2,3)
(2, 3, 2, 2) => (2,2,2,3)

由于游戏结束时序列中必然没有连续的两个+号,所以按照我们的简化记法,此时的状态为().

接下来是两个重要概念,第一是Sprague-Grundy(SG)函数g(x),其中x是“游戏图”中的一个节点(状态),如果我们用{y1, y2, ..., yn}表示x的所有子节点(即下一步状态)。那么SG函数的定义是:

g(x) = FirstMissingNumber(g(y1), g(y2), ... g(y3))

这是个递归定义的函数。如果x没有子节点,那么定义g(x) = 0;如果x有n个子节点,比如有3个子节点,g值分别为{0,1,3},那么g(x)的值是这个列表中第一个没有出现的>=0的值,此时g(x) = 2。

为什么需要引入这样一个函数?此处给出一个定理,各位暂时承认他即可:

------------

定理1:如果一个节点x的g(x)=0,那么x状态一定是“先手必输”的状态;否则一定是“先手必赢”。

------------

因此,我们如果能递归算出g(字符串初始状态)的值,那么我们就可以立即判断输赢。


但是,递归计算这个式子会比较繁琐。因为,一个连续序列经常会被分割成两个不连续的序列,这样递归计算的复杂度会比较高。所以我们引入第二个定理,即著名的Sprague-Grundy定理:

------------

定理2:如果一个游戏G由许多相同规则的子游戏G1,G2,..., Gk组成,那么对于游戏G的一个状态x = {x1,x2,...,xn},SG函数g(x) = g(x1) ^ g(x2) ^ ... ^ g(xn)。其中^表示“异或”。

------------

在我们这个游戏里,多个不相邻的+++子序列,就可以看做多个子游戏。所以,我们就可以用DP来代替递归了,比如当前状态为(10),它的后继状态可能是(8), (7), (2,6), (3,5), (4,4)。这里,我们就没有必要再次递归去算(2,6), (3,5)和(4,4),因为根据SG定理, g((2,6)) = g(2) ^ g(6), g((3,5)) = g(3) ^ g(5) ....一般地,对于各种可能状态,我们有如下的计算公式:

g(0) = 0;        // 一个+都没有,先手必输
g(1) = 0;        // 只有一个+,先手必输
g(2) = FirstMissingNumber(g(0)) = 1     // 两个+,先手必赢
g(3) = FirstMissingNumber(g(1))        = 1            // 先手必赢
g(4) = FMN(g(2), g(1) XOR g(1)) = 2    // 两种情况:A 1111->0011 OR 1100,也就是状态2,B 1111-> 1001  
...
g(9) = FMN(g(0)^g(7), g(1)^g(6), g(2)^g(5), g(3)^(4)).
...
g(x) = FMN(g(0)^g(x-2), g(1)^g(x-3), g(2)^g(x-4), ... g(x/2)^g(x/2)).

因为计算每一个g(x)都要进行x/2次异或,且FirstMissingNumber也是一个O(x)的操作,所以计算g(1)~g(x)共需要O(x^2)时间。在长为N的“棋盘”上,最大的连续+串的长也为N,所以最差时间复杂度是O(N^2)。需要额外空间保存[0, 1,2,3,4,5...N]状态下的g值,因此空间复杂度是O(N)

另外,如果最初的状态不是一个连续的+号序列,而是几个不连续的+号序列,比如x = (2,3,8)。 那么我们只要分别算x1 = (2), x2 = (3), x3 = (8)三种情况的g值,然后三者取异或即可,对时间空间复杂度没有影响。

-----------------------------------
补充下这个问题的SG函数的图。这看起来是个分形,说不定利用这个性质,能得到更优的解法:

Sprague-Grundy函数

Sprague-Grundy函数


PS:这题要是有人能在不知道SG定理的情况下现场给出多项式时间的解法,绝对是神……

本文由stellari首发于1point3acres.com论坛,图文均为原创,转载请注明。

评分

5

查看全部评分

readman 发表于 2015-7-13 12:38:51 | 显示全部楼层
象棋就不是无偏博弈,因为双方各有不同的棋子和胜利条件 <---这个有歧义, 应该说是因为双方各执颜色不同的子
回复 支持 反对

使用道具 举报

glaciersilent 发表于 2015-7-13 13:11:18 | 显示全部楼层
膜拜大神 顺便八卦下可是LC上的stellari?
回复 支持 反对

使用道具 举报

 楼主| stellari 发表于 2015-7-13 13:19:43 | 显示全部楼层
glaciersilent 发表于 2015-7-13 13:11
膜拜大神 顺便八卦下可是LC上的stellari?

见笑见笑,大神绝不敢称,但确实是LC上的同名人士。
回复 支持 反对

使用道具 举报

iFighting 发表于 2015-7-13 21:41:01 | 显示全部楼层
楼主,求交流!!带我飞!
回复 支持 反对

使用道具 举报

UmassJin 发表于 2015-7-14 01:00:58 | 显示全部楼层
感谢楼主分享! 请教楼主,定理一里面说 “如果一个节点x的g(x)=0,那么x状态一定是“先手必输”的状态;否则一定是“先手必赢”。”  可是对于这个情况,先手有可能赢(如果是1111->0110)也有可能输(1111->0011),“g(4) = FMN(g(2), g(1) XOR g(1)) = 2    // 两种情况:A 1111->0011 OR 1100,也就是状态2,B 1111-> 1001” ,但是g(4)=2,这里一定表示先手必赢的状态吗? 另外这个算法和minmax那些算法有什么不同的地方呢?
回复 支持 反对

使用道具 举报

 楼主| stellari 发表于 2015-7-14 08:03:43 | 显示全部楼层
UmassJin 发表于 2015-7-14 01:00
感谢楼主分享! 请教楼主,定理一里面说 “如果一个节点x的g(x)=0,那么x状态一定是“先手必输”的状态;否 ...

是的,大部分局面都是有可能赢,也有可能输的。但是咱们这里讨论问题的前提都是指“在最优策略下”。所以“先手必赢”的意思是:在双方都采用最优策略的情况下,先手必赢。换句话说:只要先手想赢,先手就一定能赢,不管对手有多么聪明。

抱歉,我的博弈论知识仅限于原帖中提到的那些。minmax我并不了解,容我再补习一下。
回复 支持 反对

使用道具 举报

UmassJin 发表于 2015-7-14 08:54:35 | 显示全部楼层
stellari 发表于 2015-7-14 08:03
是的,大部分局面都是有可能赢,也有可能输的。但是咱们这里讨论问题的前提都是指“在最优策略下”。所以 ...

好的,多谢楼主详细解答!一直都特别纠结于游戏design或者是algorithm之类的问题,再细细研读一下。
回复 支持 反对

使用道具 举报

zhangyichi12 发表于 2015-7-14 14:52:26 | 显示全部楼层
这这!蛮有趣的样子~
回复 支持 反对

使用道具 举报

lchen77 发表于 2015-8-27 09:57:47 | 显示全部楼层
膜拜,楼主太神啦。
从状态(2, 3, 4)开始,我们能进入的可能状态是  // 挑出其中一个数减去2,如果结果小于2,删除
(2-2, 3, 4) => (3,4)
(2, 3-1, 4) => (2,4)
(2, 3, 4-2) => (2,2,3)
(2, 3, 2, 2) => (2,2,2,3)
为什么 (2, 3, 2, 2) => (2,2,2,3) 这个可以,不是至少要flip不?why 可以一把4 拆成两个2呢?
(2, 3-1, 4) => (2,4) 这个楼主是不是笔误,应该为 (2, 3-2, 4) => (2,4) 呢?
还是我的理解有误?
回复 支持 反对

使用道具 举报

 楼主| stellari 发表于 2015-8-28 08:59:03 | 显示全部楼层
lchen77 发表于 2015-8-27 09:57
膜拜,楼主太神啦。
从状态(2, 3, 4)开始,我们能进入的可能状态是  // 挑出其中一个数减去2,如果结果小 ...

这些都是笔误,多谢指出。3-1应该是3-2,至于那个2,3,2,2,其实应该是2,3,1,1 => 2,3
回复 支持 反对

使用道具 举报

liaoyg620 发表于 2015-9-23 08:20:18 | 显示全部楼层
感谢楼主,这类型的题还是见得少了
回复 支持 反对

使用道具 举报

yaoshun 发表于 2015-10-3 04:47:40 | 显示全部楼层
楼主的推导只用了一个dp函数,有个很明显的问题。

例如++++--++的情况。4, 2 的case 按楼主的推导,应该是先手负的。但实际上可以先手胜,通过在 4 的部分强迫自己负的情况下。

我觉得至少要两个dp函数,winable和losable,然后推导一个表出来。winable[4]和losable[4]都是true,这就是这题妙处所在。
回复 支持 反对

使用道具 举报

yaoshun 发表于 2015-10-3 04:59:01 | 显示全部楼层
不好意思,说错了。

看了下nim游戏,明白楼主的意思了。楼主并没有给出sg函数在这题的具体形式,但是这个函数是存在的。
回复 支持 反对

使用道具 举报

plich 发表于 2015-10-3 06:54:40 | 显示全部楼层
赞一个先

不知道楼主能否详细解释一下定理一呢?
在给定定理一的条件下来看定理二以及后面的DP设计都挺make sense的
回复 支持 反对

使用道具 举报

kennethinsnow 发表于 2015-10-14 16:30:08 | 显示全部楼层
求定理1,2的解释。多谢
回复 支持 反对

使用道具 举报

kennethinsnow 发表于 2015-10-14 16:32:11 | 显示全部楼层
考这题是纯黑了吧
回复 支持 反对

使用道具 举报

henryisyoung 发表于 2016-7-27 23:42:49 | 显示全部楼层
看的合不拢腿了
回复 支持 反对

使用道具 举报

本版积分规则

请点这里访问我们的新网站:一亩三分地Instant.

Instant搜索更强大,不扣积分,内容组织的更好更整洁!目前仍在beta版本,努力完善中!反馈请点这里

关闭

一亩三分地推荐上一条 /5 下一条

手机版|小黑屋|一亩三分地论坛声明 ( 沪ICP备11015994号 )

custom counter

GMT+8, 2016-12-6 02:54

Powered by Discuz! X3

© 2001-2013 Comsenz Inc. Design By HUXTeam

快速回复 返回顶部 返回列表