LeetCode——464.我能赢吗

大佬,牛!!!

  • 题目:就是给你两个数,一个是我们成为n,第二个称为tar。要求两个从1到n区数,谁取出来以后,取出的数之和要是大于等于tar,那么就赢了。问一下第一个人能赢吗。
  • 我的思路:我以为一定是有规律的,然后始终没找到规律,而且本来做这种博奕类的就不太会。想了半天,直接看题解了,原来是类似暴力,然后进行剪枝。其中这个大佬的思路,让我学到了很多的东西。
  • 大佬思路:也基本是暴力,只不过这里用到了一个数组,用来存储这个情况出现过没有。并且大佬用位来记录这个数是不是取过,这跟redis的bitmaps很相似,这样可以大大降低空间复杂度。
    • 首先明确一点,就是一共有多少种情况,我们假设n是10,每个数取与不取,所以一共有2的10次方种情况。也就是1<<n,因此这个数组的长度应该是2的n次方。
    • 然后就是每种情况,我们用一个int类型的数used进行表示,例如used等于11,转换到位之后等于1011,这就可以表示我们取了第1,3,4个。所以每次进行判断之前,我们先看dp数组中的第used位置的数是不是已经计算过了。
    • 然后就是dp数组中的内容了,初始化是0,1表示先手的人能赢,2表示输,0表示还没有进行计算。
    • 然后就是递归了(递归是本次user,能不能赢,把所有的没拿的都遍历一下就行了),递归就是本人拿i赢了,或者下一个人拿i输了,这时候就能将dp[used]设置为1,并且return即可。
    • 然后遍历完所有的,都没有赢,则输掉。
  • 技巧:这里技巧还是挺多的,基础就是深度优先,然后用一个数组进行标记是不是使用过,但是这个数组比较难定义和判断,大佬用位来进行判断,这个思路简直绝了。里面还有一些位移运算、逻辑运算等。

java代码

class Solution {
    public boolean canIWin(int maxChoosableInteger, int desiredTotal) {
        // 目标是desiredTotal,
        if (maxChoosableInteger >= desiredTotal) {
            return true;
        }
        if ((maxChoosableInteger + 1) * maxChoosableInteger / 2 < desiredTotal) {
            return false;// 谁也不会赢
        }
        char[] dp = new char[1 << maxChoosableInteger];// 1表示能赢
        return process(maxChoosableInteger, desiredTotal, 0, 0, dp) == 1;
    }


    /**
     * 这里注意递归的目的,一次递归就是找到拿哪一个能赢
     * @param n    表示最大的值
     * @param tar  表示目标值,也就是界限
     * @param used used是一个数,这个数用位来表示第i个数是不是使用,也就是说used一样,则表示已经出现过
     * @param cur  表示当前的求和
     * @param dp   是一个数组,用来记录这种情况是不是出现过
     * @return 返回的是一个char,
     */
    public char process(int n, int tar, int used, int cur, char[] dp) {
        if (dp[used] != 0) {// 之前遇到过了,
            return dp[used];
        }
        for (int i = 0; i < n; i++) {
            if (((used >> i) & 1) != 0) {// 这一个数已经被选过了
                continue;
            }
            // 自己拿第i个赢了,或者下一个人拿第i个输了
            if (cur + i + 1 >= tar || process(n, tar, used | (1 << i), cur + i + 1, dp) == 2) {
                dp[used] = 1;
                return 1;
            }
        }
        dp[used] = 2;
        return 2;
    }
}
  • 总结:题目的思路就是暴力求解了,只不过过程中可以进行剪枝,因为有一些情况我们已经计算过了。关键是大佬用位来进行标记,从而将一个二维数组变成了一维,并且也方便了比较的过程,真的是学到了。上大佬链接
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值