又是一道top-down+memo的题目,依旧,没有做出来。
几个问题:
1.脑袋里没有top-down的思路。想到了是dp,但是依然在哪里dp[i][j]状态是什么想半天。
2.这道题的状态不是之前dp[i][j]这种的,而是存在以key(用used算出来的)的形式出现的。
但是状态还是有的,状态就是map[key]:在当前used的情况下,先动手的能不能赢。
因为之前都是dp[i], dp[i][j]之类的。这里不太好写成那样,所以不太习惯。
3.两个人游戏,想到了状态是之前那种:在某个条件下,先动手的人能不能赢。但是这个条件是什么?这里应该是:在留有一些没有选择的数,还剩下的desiredTotal。但是这两个状态是一回事,因为还剩下哪些没有选择的数,用总的desiredTotal减去已经选择了的数,就可以得到剩下的desiredTotal了。所以可以用还剩下哪些数没有选择来表示当前的状况。这个状况其实就是dp的状态,用作map的key。
4.memo这里用来记忆的是用的map,其实应该都用map来记忆吧。
5.这里是状态不好表达出来的一个示例,这里就直接将状态放进数组,进行处理之后作为新的状态
class Solution {
public:
bool canIWin(int maxChoosableInteger, int desiredTotal) {
if (desiredTotal <= 0)
return true;
int sum = ((1 + maxChoosableInteger) / 2) * maxChoosableInteger;
if (sum < desiredTotal)
return false;
vector<bool> used(maxChoosableInteger+1, false);
unordered_map<int, bool> m;
return helper(desiredTotal, used, m);
}
private:
bool helper(int desiredTotal, vector<bool>& used, unordered_map<int, bool>& m) {
if (desiredTotal <= 0)
return false;
int key = format(used);
if (m.find(key) == m.end()) {
for (int i = 1; i < used.size(); ++i) {
if (used[i])
continue;
used[i] = true;
if (!helper(desiredTotal-i, used, m)) {
m[key] = true;
used[i] = false;
return true;
}
used[i] = false;
}
m[key] = false;
}
return m[key];
}
int format(vector<bool>& used) {
int ret = 0;
for (bool b : used) {
ret <<= 1;
if (b)
ret |= 1;
}
return ret;
}
};
小技巧:把bool数组化为一个int,用来表示map中的key。
一个总的思路就是说:
helper函数:在还剩下一些数可以用的情况下(used表示),先动手的这个人能不能赢。
如果可用的这些数中,没有一个选了能赢,那么返回false;有一个能赢,就返回true。
另外,还有一个注意点就是:
这里的辅助函数往往都是返回相应状态的值的。也就是说,可以把这个函数就当作map来用,给你一个key,你返回给我对应的值。在helper内部也是这么用它的。我觉得这点是比较重要的。把辅助函数当作一个map的get方法来用的思路。培养这种直觉。
从做题的角度来看,应该这么思考:
定义一个函数,把它当作map的get方法。如果map变量中有相应的key(也就是状态)的话(也就是这个状态计算过了),就直接返回value。否则,将它计算出来(在计算中,如果需要知道某个子状态的相应value,递归调用自身就可以了),并且存入map变量,再返回这个value。
这个思路应该是做这类top-down+memo思路中最重要的了
这类题还需要多做几遍。
另外几个类似的题目:
stone game一和二.
375. Guess Number Higher or Lower II
stone game1的子状态是:从i到j堆时,第一个拿的人是不是能够赢
stone game2的子状态是:指定开始位置和M的情况下,第一个拿的人最多拿多少。总共的减去第二个人拿的。
can i win的子状态是:在有一些数字没用到(used指示)以及当前当前的desiredTotal(也是used指示)情况下,第一个拿的人能不能赢
这些其实都是普通的子状态(而且都是直接的子状态,不需要绕弯的),只是top-down的方法更加方便(更符合思考逻辑),由底向上不太好写,所以用了它
另一道相似的题目:
https://blog.csdn.net/weixin_43462819/article/details/100888259