九章算法 | Microsoft 面试题:我能赢

撰文 | JZ
专栏 | 九章算法

题目描述

有两个人玩一个游戏,给定一个最大可取整数 maxChoosableInteger,两个人轮流从1~maxChoosableInteger 中取一个数,取过的数不可再取,若其中一方取过以后,
所有取过的数加起来的和大于等于desiredTotal,那么这个人获胜。
现在给你maxChoosableInteger和desiredTotal,问先手是否必胜,假定游戏双方均采取最优策略。
你可以假定给出的 maxChoosableInteger不超过20,desiredTotal不超过300。


样例

        输入:
maxChoosableInteger = 10
desiredTotal = 11

输出:
false
      

样例说明

        无论先手怎么取数,先手都会输掉游戏。
先手可以取1到10中的任何一个。如果先手取1,那么后手可以取2到10中的任何一个。
后手如果取10,那么就可以赢得游戏,因为此时和为1+10=11=desiredTotal。
假如先手取其他的数,那么后手仍然能赢得游戏,只要使和大于等于11即可。
      


解题思路

1.我们读完题发现这题似乎是一题经典的博弈问题,但是有一个很重要的限制条件————每个数只能取一次。

有了这个条件,我们很容易就能想到所有的取数方案,最多有maxChoosableInteger! 种,即 1~maxChoosableInteger 的全排列。

但是一一枚举这maxChoosableInteger!种方案明显超过了能承受的时间复杂度。


2.经过对这maxChoosableInteger!种方案的思考,我们可以发现,

虽然方案有阶乘的数量级,但是这么多方案有很多的部分重复计算了,

类似前缀和的思想,我们计算1~n的前缀和,只要计算出1~n-1的前缀和再加上n即可,避免了多余的计算。

这里我们同样可以把之前计算的结果保留下来以方便之后的计算,我们称之为记忆化搜索,

和前缀和不同的是,这里用的是自顶向下(类似于分治的把大问题拆分为小问题的求解方法)的求解方法。


3.在记忆化搜索中,使用了两个条件判断胜利,

一是当前剩下的desiredTotal(减去了取走的数)小于等于0,

二是对于剩下的还未取的数,已经搜索过且是必胜的状态,

假如这个状态没有搜索过,就进行dfs判断这个状态,直到遇到判断过的状态或desiredTotal小于等于0。

其实本题也可以用自下而上的动态规划实现,状态表示和记忆化搜索相同。


4.对于记录状态的数组,

我们使用01向量表示,0代表这个数没有使用过,1代表已经使用过。

把1~maxChoosableInteger的使用情况定义为一个只有0,1的向量,

比如maxChoosableInteger=3,他们的使用情况有8种,分别是000(没有元素被使用),001,010,100,011,110,101,111(所有元素都被使用)。

再把这个向量转化成十进制数作为下标。


5.设maxChoosableInteger为n,
除了特殊情况外我们需要计算所有的状态,且每个状态只需要计算一次,则时间和空间复杂度均为O(2^n)。


参考代码

v2-9a40b859e57c611693a647e1e49deda5_b.jpg


面试官角度

本题运用了记忆化搜索的方法,

能用记忆化搜索解出的能获得hire的评价,

如果能把状态压缩为一个十进制数,则能获得strong hire的评价。


Lintcode相关题目

不同的二叉查找树

交叉字符串


九章参考答案

九章算法 - 帮助更多中国人找到好工作,硅谷顶尖IT企业工程师实时在线授课为你传授面试技巧

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值