[leetcode]Can I Win
链接:https://leetcode.com/problems/can-i-win/description/
Question
In the “100 game,” two players take turns adding, to a running total, any integer from 1..10. The player who first causes the running total to reach or exceed 100 wins.
What if we change the game so that players cannot re-use integers?
For example, two players might take turns drawing from a common pool of numbers of 1..15 without replacement until they reach a total >= 100.
Given an integer maxChoosableInteger
and another integer desiredTotal
, determine if the first player to move can force a win, assuming both players play optimally.
You can always assume that maxChoosableInteger
will not be larger than 20 and desiredTotal
will not be larger than 300.
Example
Input:
maxChoosableInteger = 10
desiredTotal = 11
Output:
false
Explanation:
No matter which integer the first player choose, the first player will lose.
The first player can choose an integer from 1 up to 10.
If the first player choose 1, the second player can only choose integers from 2 up to 10.
The second player will win by choosing 10 and get a total = 11, which is >= desiredTotal.
Same with other integers chosen by the first player, the second player will always win.
Solution
class Solution {
public:
int maxn;
vector<unordered_map<int, bool> > dp;
bool canIWin(int maxChoosableInteger, int desiredTotal) {
maxn = maxChoosableInteger;
if (maxn >= desiredTotal) return true;
if (maxn*(maxn+1)/2 < desiredTotal) return false;
dp.resize(desiredTotal+1);
return canWin(desiredTotal, 0);
}
bool canWin(int target, int visited) {
// 表示target和visited状态已经被访问过
if (dp[target].find(visited) != dp[target].end()) {
return dp[target][visited];
} else {
for (int i = 1; i <= maxn; i++) {
// 使用mask进行挑数
int mask = 1<<i;
// 表示i没有访问过
if ((mask & visited) == 0 &&
// 表示可以直接拿i就赢了 或者是 对手输了
(i >= target || !canWin(target-i, visited | mask))) {
dp[target][visited] = true;
return true;
}
}
}
dp[target][visited] = false;
return false;
}
};
思路:这道题是博弈题,也就是双方都很不傻的情况下如何考虑进行博弈的问题。CanIWin函数中直接将两种简单情况去掉,一种是可以选择的数字大于目标值(必定赢),一种是所有数字加起来也达不到target(必定输)。下一步调用CanWin函数进入下一步的递归(CanWin不是针对自己来说的,对手赢了CanWin也是返回true)。这里递归步骤多会超时,所以需要进行状态压缩【保存子问题的解的,主要思想是把所有可能的状态(子问题)用一个数据结构(通常是整数)统一表示,再用map把每个状态和对应结果关联起来,这样每次求解子问题时先find一下,如果map里面已经有该状态的解就不用再求了;同样每次求解完一个状态的解后也要将其放入map中保存】。每个状态不同之处有两个:
- target
- 可以选的数(这里的数是不放回地选择)
所以这里使用vector[target][visited]进行标记即可。