今天刷了一道非常好的dp题(位运算状态压缩)

请看题

此题有两个难点

  1. 状态不好找,两个人轮流取数,取出的数字不放回,我们很容易被两个人取数给绕晕,其实我们只要关心第一个取数的人胜利就行,当第一个人取数时,若取出的数大于等于desiredToal,那么胜利,否则转移到下一个状态,即另外一个人取数的状态。我们可以知道,上一个人胜利的条件是下一个人失败。所以状态转移方程就很容易出来了。

  2. 如何保存状态取值。我们观察可以发现,随着每次取出数字。剩余的数字在减少。恰好取出的数字能够标志当前状态。比如之前取出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);
    }
};

这道题还是干货满满的,很值得收藏!!!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值