leetcode 464. 我能贏嗎
阿新 • • 發佈:2018-12-20
在 “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;
}
};