LeetCode:464. Can I Win

在这里插入图片描述

又是一道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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值