经典博弈

描述
有两个人玩游戏,给定一个最大可取代数maxChoosableInteger,两个人轮流从1~maxChoosableInteger中取一个数,取过的数不可再取,若其中一方取过以后,所有取过的数的和大于等于desiredTotal,那么这个人获胜。现在给maxChoosableInteger和desiredTotal,问先手是否必胜,假定游戏双方均采取最优策略。你可以假定给出的maxChoosableInteger不超过20,desiredTotal不超过300。


样例
输入:
maxChoosableInteger = 10
desiredTotal = 11
输出:
false


说明
无论先手怎么取数,先手都会输掉游戏。先手可以取到1到10中的任何一个。如果先手取1,那么后手可以取2到10中任何一个。后手如果取10,那么就可以赢得游戏。假如先手取其它的数,那么后手仍然能赢得游戏。


思路
明显的是,这个全排列问题不能用枚举法来做。1~n的前缀后就是1~n-1的和加上n,用记忆化搜索判断胜利情况。要么当前剩下的desiredTotal小于等于0,要么对于剩下的还未取得数,已经搜索过且是必胜的状态。假设这个状态没有搜索过,就进行判断这个状态,直到遇到判断过的状态或desiredTotal小于等于0。


代码

#include "stdafx.h"
#include<iostream>
#include<vector>
#include<algorithm>
using namespace std;
class Solution {
public:
    vector<int> dp;
    vector<bool> used;
    bool boyi(int maxChoosableInteger, int desiredTotal) {
        int sum = (1 + maxChoosableInteger)*maxChoosableInteger / 2;
        if (sum < desiredTotal) {
            return false;
        }
        if (desiredTotal <= maxChoosableInteger) {
            return true;
        }
        dp.resize(1 << maxChoosableInteger);
        fill(dp.begin(),dp.end(),-1);
        used.resize(maxChoosableInteger + 1);
        fill(used.begin(), used.end(), 0);
        return handler(desiredTotal);
    }
    bool handler(int desiredTotal) {
        if (desiredTotal <= 0)
            return false;
        int key = fmt(used);
        if (dp[key] == -1) {
            for (int i = 1; i < used.size(); i++) {
                if (!used[i]) {
                    used[i] = true;
                    if (!handler(desiredTotal - i)) {
                        dp[key] = -1;
                        used[i] = false;
                        return true;
                    }
                    used[i] = false;
                }
            }
            dp[key] = 0;
        }
        return dp[key] == 1;
    }
    int fmt(vector<bool> & used) {
        int num = 0;
        for (int i = 1; i < used.size(); i++) {
            num <<= 1;
            if (used[i]) {
                num |= 1;
            }
        }
        return num;
    }
};

int main()
{
    int maxChoosableInteger, desiredTotal;
    cin >> maxChoosableInteger >> desiredTotal;
    Solution sol;
    bool result=sol.boyi(maxChoosableInteger, desiredTotal);
    if (result) {
        cout << "true" << endl;
    }
    else {
        cout << "false" << endl;
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值