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

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

本題解法如下:

1、首先讓第一個人開始選擇,把第一個人能夠選的所有的情況都便利一遍,如果所有的情況都無法贏,則返回false,如果有一個情況是可以贏的,那麼就返回true。

2、贏的方式有兩種:自己的選擇的數字大於等於目標數字;在自己選這個數字的情況下,對手無法贏。判斷對手是否可以贏的方法和1相同,不同的是,自己選過的數字對手無法選擇。

3、本題記錄某個數字是否選過了,可以用一個Int型別的數字記錄(用其2進位制位記錄,如果選擇了第一個數字,則這個int型的數字的第一個二進位制位為1),如果選擇過了,則不能重複選擇

4、記錄每次選擇的結果,因為第一個人選1,第二個人選2 和 第二個人選1,第一個人選二結果是相同的。

c++程式碼和註釋如下:

class Solution {
public:
    // 用一個vector記錄每次的選擇的結果,如果能贏則為1,不能贏則為-1,沒有記錄則為0,比如:
// 第一個人選1,第二個人選2或者第一個人選2第二個人選1的前提下, // 二進位制位為:0000 0110,則v_c[6]的值如果不為零,為1則返回true,為-1則返回false vector<char> v_c; int M, T; bool canIWin(int MM, int TT) { // 這裡面進行判斷,是否可以得到答案,如果整個陣列的和小於要求的目標數字,則返回false M = MM; T = TT; if ((1 + M) * M / 2 < T) return false; // 這裡注意初始化,所有要遍歷的數字為2^M個,因此下標要加1 v_c = vector<char>(1 << (M) + 1, 0); return canWin(T, 0); } bool canWin(int target, int visited) { // 如果visited這種模式已經有人選過了,那麼返回結果即可 // 如果沒有人選過這種模式,那麼在這種模式下把可以選擇的數字都選一遍,逐個判斷 if (v_c[visited] != 0) return v_c[visited] == 1; for (int i = 1; i <= M; ++i) { // 用一個mask記錄當前選擇了第幾個數字 int mask = (1 << i); // 如果mask&visited的結果是0那麼第i個數字(也就是i)沒有選過 // 贏的條件:如果選的這個數大於等於目標數字,或者對手在這種情況下是輸的,那麼,我在這情況下就是贏的 // 要知道對手在這個情況下是不是輸的,就遞迴canWin(target - i, visited | mask)得到答案即可 if ((mask & visited) == 0 && (i >= target || !(canWin(target - i, visited | mask)))) { v_c[visited] = 1; return true; } } // 如果在visited這個模式(前提)下遍歷了一圈可以選擇的數字都無法贏,那麼我就輸了 v_c[visited] = -1; return false; } };