LeetCode 464 我能赢吗(博弈)

题目:

示例 :

输入:maxChoosableInteger = 10, desiredTotal = 11
输出:false
解释:
累计和为俩选手共同的和;
无论第一个玩家选择哪个整数,他都会失败。
第一个玩家可以选择从 1 到 10 的整数。
如果第一个玩家选择 1,那么第二个玩家只能选择从 2 到 10 的整数。
第二个玩家可以通过选择整数 10(那么累积和为 11 >= desiredTotal),从而取得胜利.
同样地,第一个玩家选择任意其他整数,第二个玩家都会赢。(不管第一个玩家选择哪个数字都不会
达到目标数,而不管 第一个玩家选择哪个数字,第二个玩家总能在剩下的数字中找到一个数字使总和
达到目标数)

思路:

这里先假设一定有一个选手获胜,一个选手失败!

我们可以定义目前的总和数为sum来判断是否获胜;总和指共同总和而不是各自总和。

我们可以从中发现,当当前的总和数sum加上当前选手选择的数后,若其值大于或等于目标值(desiredTotal)则获得胜利;

即便当前总和数sum无法达到目标值,但随着俩位选手的最优选择后,sum会逐渐增大直到达到目标值,如此我们便可以采用递归:

        每一层都在上一层的基础上加上自己的最优解再进行判断

由于我们是要考虑先手是否获胜,所以可以采用

        1.当前总和加当前选择值是否大于目标值;

        2.对手是否必定失败

这里有一点需要注意的是,什么是最优解?由于找不到规律所以我们只能遍历所有解而且在遍历中不返回false,只返回true,除非所有值都被遍历任未返回true值,我们才能返回false值;

大致代码如下:

  for(int i=1;i<=maxChoosableInteger;i++){//遍历以获得最优解

                if(sum+i>=desiredTotal){//当前总和达到目标值

                    return true;

                }

                if(!dfs(sum+i,maxChoosableInteger,desiredTotal)){//如果对手必输即对手返回false

                    return true;

                }

            }

 return false;

接下来我们要注意的是,题目要求值不放回,也就是每个值只能被拿一次;那么我们可以定义一个二进制数state来存储对应值是否被拿,如二进制的10可以表示1已经被使用,二进制的1000可以表示3已经被使用,二进制的1010可以表示1和3已经被使用,在循环中如果遇到已经使用过的值那么我们直接continue

在递归或循环之中我们总会遇到已经判断过的情况,而不同的情况是由state和当前总和sum决定,而当前总和也可以由state得到,故我们可以定义一个数组其下标为state的不同值来实现记忆化搜索

前面我们讲到假设必定有一人胜利,一人失败,那么接下来我们就得考虑其它情况:

        如果俩人将所有值都拿了以后仍然无法达到目标值,那么依照题意我们直接返回false

我们也不难发现,如何目标值小于可以选择的最大整数,那么先手必定获胜直接返回true。

下面是完整代码:

        

  • 11
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值