【Lintcode】985. Can I Win

题目地址:

https://www.lintcode.com/problem/can-i-win/description

有一个游戏,两个人轮流从一个数字池里取数,数字池里一开始的数字是 1 ∼ m 1\sim m 1m,每个数只能被取一次。另给定一个数 d d d,如果某人取完数之后,两个人取的数的总和达到或超过了 d d d,则最后取数的那个人就赢了。问先手是否有必胜策略。

思路是记忆化搜索。这里可以考虑状态压缩,设 s s s从左向右的第 k k k个二进制位是 1 1 1的话表示 k k k这个数还没被取,否则表示这个数被取了。设 f [ s ] f[s] f[s]表示当前先手面对的局面是 s s s的情况下,先手是否必胜。那么,当他面对的局面里,已经取的数总和已经达到了 d d d的话,则必输(意味着对方上次取完数之后他就已经赢了)。否则可以枚举先手取哪些数,则有: f [ s ] = ⋁ i = 2 k ∧ ( s > > k & 1 = 0 ) ¬ f [ s + i ] f[s]=\bigvee_{i=2^k \land (s>>k\&1=0)} \neg f[s+i] f[s]=i=2k(s>>k&1=0)¬f[s+i]这里的意思是只要先手取某个数就能导致对方必输,那么他就必赢。代码如下:

import java.util.Arrays;

public class Solution {
    /**
     * @param maxChoosableInteger: a Integer
     * @param desiredTotal: a Integer
     * @return: if the first player to move can force a win
     */
    public boolean canIWin(int maxChoosableInteger, int desiredTotal) {
        // Write your code here
        // 特判两个必赢和必输的局面
        if (maxChoosableInteger >= desiredTotal) {
            return true;
        }
        if ((1 + maxChoosableInteger) * maxChoosableInteger / 2 < desiredTotal) {
            return false;
        }
        
        // dp[s]表示先手面对的局面是s的情况下是否必胜,如果是则取1,否则取0
        int[] dp = new int[1 << maxChoosableInteger];
        
        // 一开始赋值为-1表示还没求出来
        Arrays.fill(dp, -1);
        
        return dfs(dp, 0, 0, maxChoosableInteger, desiredTotal);
    }
    
    // 返回先手面对的局面是state,已经取的数总和是curSum的情况下,他是否必胜
    private boolean dfs(int[] dp, int state, int curSum, int maxInt, int desiredTotal) {
    	// 如果有记忆了则调取记忆
        if (dp[state] != -1) {
            return dp[state] == 1;
        }
        
        // 如果已经取的数超过了desiredTotal,那先手已经输了,返回false
        if (curSum >= desiredTotal) {
            dp[state] = 0;
            return false;
        }
    
    	// 开始枚举先手取哪个数
        for (int i = 1; i <= maxInt; i++) {
        	// 如果枚举到已经取了的数则跳过
            if (((state >> i - 1) & 1) != 0) {
                continue;
            }
            
            // 枚举取该数。如果先手取了i,会导致后手(这个时候后手变成了先手)
            // 面对state | (1 << i - 1)这个局面的时候必输,那么先手就赢了
            if (!dfs(dp, state | (1 << i - 1), curSum + i, maxInt, desiredTotal)) {
                return true;
            }
        }
        
        // 如果枚举完所有情况先手仍然没法赢,则先手必输,返回之前记忆一下
        dp[state] = 0;
        return false;
    }
}

时空复杂度 O ( 2 m ) O(2^m) O(2m)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值