题意简述:给定两个值n和desiredTotal,要求两位玩家从整数1,2,……,n中依次取数,每个数只能取一次,每个取出的数都会加到一个和数上,最先使和数大于或等于desiredTotal的玩家获胜。问玩家1(先取数的)是否必胜。假设两位玩家都会作出最优选择。
输入:两个值n和desiredTotal。
输出:一个bool值,表示玩家1是否获胜。
示例:对于n=10和desiredTotal=11,玩家1必败,因为无论取1到10哪个数,玩家2都能再取一个数使和达到11。
题解:
采用dfs+map辅助记忆化搜索。使用二进制串记录当前状态下每个数有没有被拿(数可用为1,不可用为0)。dp(bits, remain)为true,当且仅当对于bits里面所有可用的i,存在dp(bits & ~(1 << i), remain-i)为false。简单的解释:对于当前状态,只要在所有的取法中存在至少一种取法使对方必败,那么这个状态是必胜的。终结状态是remain <= 0,这意味着已经取的数之和已经超过desiredTotal,因而是必败状态。
使用unorder_map记录dfs过程中产生中间状态dp的值。注意unorder_map的key值可以只用bits,这是因为remain可以通过desiredTotal减去已取的数(bits对应位为1)之和得到。
算法实现如下,空间复杂度是O( 2n ),因为状态数就是 2n 。在这题中n<=20,因此 2n 是可接受的。
class Solution {
private:
unordered_map<unsigned long, bool> mymap;
int _maxInteger;
bool dp(bitset<20>& mybit, int remain) {
if(mymap.find(mybit.to_ulong()) != mymap.end())
return mymap[mybit.to_ulong()];
if(remain <= 0) {
mymap[mybit.to_ulong()] = false;
return false;
}
for(int i = 1;i <= _maxInteger;i++) {
if(!mybit[i-1]) {
mybit.set(i-1);
bool temp = dp(mybit, remain-i);
mybit.reset(i-1);
if(!temp) {
mymap[mybit.to_ulong()] = true;
return true;
}
}
}
mymap[mybit.to_ulong()] = false;
return false;
}
public:
bool canIWin(int maxChoosableInteger, int desiredTotal) {
_maxInteger = maxChoosableInteger;
bitset<20> mybit;
mybit.reset();
if(desiredTotal <= 0) return true;
if((1+maxChoosableInteger)*maxChoosableInteger/2 < desiredTotal)
return false;
return dp(mybit, desiredTotal);
}
};