请看题
此题有两个难点
-
状态不好找,两个人轮流取数,取出的数字不放回,我们很容易被两个人取数给绕晕,其实我们只要关心第一个取数的人胜利就行,当第一个人取数时,若取出的数大于等于desiredToal,那么胜利,否则转移到下一个状态,即另外一个人取数的状态。我们可以知道,上一个人胜利的条件是下一个人失败。所以状态转移方程就很容易出来了。
-
如何保存状态取值。我们观察可以发现,随着每次取出数字。剩余的数字在减少。恰好取出的数字能够标志当前状态。比如之前取出2,3,1和之前取出3,1,2对接下来的结果的影响是一样的,所以我们应该像一个方法将n个不相等的数字序列压缩成一个数字。这就用到我们的位运算了。将这些数字的序列转换成对应位的1。比如2,1,3就变成0000111。5,1,3就是0010101。
到这里两个难点就解决了,接下来请看代码
class Solution {
public:
bool search(vector<int> &tags, int maxNum, vector<int> &dp, int state){
if(dp[state]!=-1){
return dp[state];
}
int n = tags.size();
for(int i=1; i<n; i++){
if(tags[i] == 0){
tags[i]=1;
int st = 1<<(i-1);
if(maxNum-i<=0 || !search(tags, maxNum-i, dp, state|st)){
tags[i]=0;
dp[state] = 1;
return true;
}
tags[i]=0;
}
}
dp[state] = 0;
return false;
}
bool canIWin(int maxChoosableInteger, int desiredTotal) {
if(maxChoosableInteger>=desiredTotal){
return true;
}
if(maxChoosableInteger*(1+maxChoosableInteger)/2 < desiredTotal){
return false;
}
vector<int> tags(maxChoosableInteger+1, 0);
vector<int> dp((1<<maxChoosableInteger) - 1, -1);
return search(tags, desiredTotal, dp, 0);
}
};
这道题还是干货满满的,很值得收藏!!!