题目
知识点
思路
首先读懂题意:
两个人是共享的和,谁取数第一个满足要求(> desiredTotal),谁赢 是否能稳赢:就是先手A出了某个数,那么后手B出了任何数都不能赢,如果后手有一种赢的方式,那么就不是稳赢 假设两位玩家游戏时都表现最佳:不存在“不让对方/说没有发现可以赢”情况,即如果存在赢的方式,一定会选择该方式赢得游戏 那么我们通过回溯即可,首先先手A选择某个数(1~maxChoosableInteger )我们进行递归搜索所有的可能。如果元素没有被访问过(Line 12 ),记录访问状态向下搜索。 重点: 向下搜索对返回结果进行判断(Line 15 ),此时后手在递归时就是作为先手,如果他作为先手赢了,那么说明原本的先手输了(后手找到了一种赢的可能),那就不是必赢,返回false,跳出寻找 直接回溯时间复杂度太高会超时 -> 记忆化搜索:
上述过程中,我使用了bitset
记录状态,每一个位表示一个元素的使用情况。因此,当前的状态转换成10进制就是唯一的,使用一个map
所有已经访问过的状态。 为什么要记录访问过的状态避免重复查找? 因为在搜索过程种,出现了很多重复的状态,比如:第一次取m,第4次取n,而在某次搜索过程中,第一次取n,第5次取m。这道题的取法的先后次序并不影响输赢,不管怎么取的顺序,只要是一样的数,如果已经搜索过不能稳赢,那么换了次序取还是不能稳赢。
代码
class Solution {
public :
int mx, ed;
unordered_map< unsigned long long , bool > m;
bool dfs ( int sum, bitset< 20 > vis) {
if ( ed <= sum) return true ;
if ( m. count ( vis. to_ullong ( ) ) ) return m[ vis. to_ullong ( ) ] ;
bool ans = true ;
for ( int i = 1 ; i <= mx; i++ ) {
if ( vis[ i] ) continue ;
bitset< 20 > u = vis;
u[ i] = true ;
if ( dfs ( sum + i, u) ) {
ans = false ;
break ;
}
}
return m[ vis. to_ullong ( ) ] = ans;
}
bool canIWin ( int maxChoosableInteger, int desiredTotal) {
mx = maxChoosableInteger, ed = desiredTotal;
if ( ( mx + 1 ) * mx * 0.5 < ed) return false ;
for ( int i = 1 ; i <= mx; i++ ) {
bitset< 20 > vis;
vis[ i] = true ;
if ( dfs ( i, vis) ) return true ;
}
return false ;
}
} ;