1. 程式人生 > >464. 我能贏嗎

464. 我能贏嗎

在 "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),從而取得勝利.
同樣地,第一個玩家選擇任意其他整數,第二個玩家都會贏。

========================================================

解題思路: 遞迴,動態規劃,DFS,備忘錄

本題使用一個int值的二進位制對應位置上的1,0來表示數字是否用過

其實用一個byte陣列也可以...

很簡單的遞迴思想

貼一下程式碼,程式碼有對應的註釋可以看一下

還想著把boolean陣列替換成bitset,但是陣列可以表示3種狀態,true,false,null,都可以減少運算成本,因此不可替換

class Solution {
    public boolean canIWin(int maxChoosableInteger, int desiredTotal) {
        //如果目標數<=選擇數,一次就贏,返回true
        if (desiredTotal <= maxChoosableInteger) {
            return true;
        }
        //獲得總數之和,選擇數總數比目標數小,結果不可達,返回false
        if (maxChoosableInteger*(maxChoosableInteger + 1)/2 < desiredTotal) {
            return false;
        }
        Boolean[] dp = new Boolean[1 << maxChoosableInteger];
        return doResolve(0, maxChoosableInteger, desiredTotal, dp);
    }
    private boolean doResolve(int flag, int chooseArea, int total, Boolean[] dp) {
        if (total <= 0) {
            return false;
        }
        //dp代表已經使用了哪個數字情況下是否能贏
        if (dp[flag] == null) {
            dp[flag] = false;
            int comp = 1;
            for (int i = 1; i <= chooseArea; i++) {
                int used = flag | comp;
                //如果,我能用這個數,且用了這個數對方贏不了
                if (used != flag && !doResolve(used, chooseArea, total - i, dp)) {
                    dp[flag] = true;
                    break;
                }
                comp <<= 1;
            }
        }
        return dp[flag];
    }
}