leetcode 464. 我能赢吗?

leetcode 464. 我能赢吗?

原题链接:https://leetcode-cn.com/problems/can-i-win

在 “100 game” 这个游戏中,两名玩家轮流选择从 1 到 10 的任意整数,累计整数和,先使得累计整数和达到 100 的玩家,即为胜者。

如果我们将游戏规则改为 “玩家不能重复使用整数” 呢?

例如,两个玩家可以轮流从公共整数池中抽取从 1 到 15 的整数(不放回),直到累计整数和 >= 100。

给定一个整数 maxChoosableInteger (整数池中可选择的最大数)和另一个整数 desiredTotal(累计和),判断先出手的玩家是否能稳赢(假设两位玩家游戏时都表现最佳)?

你可以假设 maxChoosableInteger 不会大于 20, desiredTotal 不会大于 300

示例
输入:
maxChoosableInteger = 10
desiredTotal = 11

输出:
false

解释:
无论第一个玩家选择哪个整数,他都会失败。
第一个玩家可以选择从 1 到 10 的整数。
如果第一个玩家选择 1,那么第二个玩家只能选择从 2 到 10 的整数。
第二个玩家可以通过选择整数 10(那么累积和为 11 >= desiredTotal),从而取得胜利.
同样地,第一个玩家选择任意其他整数,第二个玩家都会赢。

输入
maxChoosableInteger = 10
desiredTotal = 5

输出
true

解释:
只要第一个玩家选择 >= 5的数,都可以稳赢

输入:
maxChoosableInteger = 5
desiredTotal = 100

输出:
false

解释:
无论两个玩家怎么选择,都不可能达到100,因此都算输

稳赢的意思只要存在一个数,当我选择这个数,无论对方怎么选择,我都稳赢。
稳输的意思是,穷尽所有选择,都无法找出一个数能让自己稳赢。

当maxChoosableInteger >= desiredTotal 时,第一个人先选择,那么一定稳赢,
当所有数字的累计和 < desiredTotal时,第一个人无论怎么选,两个人都是输,因为不可能达到

根据定义,如果第一个人要稳赢,那么只要在选择了一个数之后,对方稳输就可以了,所以这是一个递归问题,不过我们需要记录哪些数字被选择了,这里采用了一个小技巧,使用位运算来存储当前哪些数字被选择
如果maxChoosableInteger = 1, 那么当1被选择,我们用1表示,未被选择用0表示
如果maxChoosableInteger =2, 如果都未选择用00表示,如果1被选择用01表示,2被选择用10表示,如果都被选择,则用11表示。

因此 选择第i个数,可以用state | 1 << i-1来表示,而判断第i个数是否被选择,可以用 (state & 1 << i-1) == 0来判断,等于0则未被选择,不等于0则已经被选择过了。
定义dp数组,用来存储在状态state下,是否能够稳赢,因为递归会有很多重复计算产生,所以通过dp数组消除重复计算。

下面上代码:

class Solution {
    public boolean canIWin(int maxChoosableInteger, int desiredTotal) {
        if(maxChoosableInteger > desiredTotal){
            return true;
        }
        if((maxChoosableInteger+1)*maxChoosableInteger/2 < desiredTotal){
            return false;
        }

        return helper(maxChoosableInteger,desiredTotal,new Boolean[(1 << maxChoosableInteger)], 0);
    }

    private boolean helper(int maxChoosableInteger, int desiredTotal, Boolean[] dp, int state){
        if(dp[state] != null){
            return dp[state];
        }

        for(int i=1; i<=maxChoosableInteger; i++){
            //如果i已经使用过,continue;
            if(((1 << i-1) & state) != 0){
                continue;
            }

            // 当前的值大于等于目标值的时候,说明稳赢
            // 或者 当选择了当前值, 对方稳输的话,自己也是稳赢。
            int curState = (1 << i-1) | state;
            if(i >= desiredTotal || !helper(maxChoosableInteger, desiredTotal-i, dp, curState)){
                dp[state] = true;
                return dp[state];
            }
        }

        return dp[state] = false;
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值