博弈题用自顶向下带memo的递归写法,比自底向上的dp写法更清晰

能赢吗

bool canIWin(int maxInt, int desired) {
    if (maxInt >= desired) return true;
    if (maxInt * (maxInt + 1) / 2 < desired) return false;
    // 因为1<=maxInt<=20,memo需要1<<20大小
    // memo[i]:0 未计算、1 win、-1 lose
    vector<int> memo(1 << 20);
    // 把哪些数用过编码到maskUsed
    function<bool(int,int)> dp = [&](int desired, unsigned maskUsed) {
        if (desired <= 0) return false; // 对方已拿到desired
        if (memo[maskUsed] != 0) return memo[maskUsed] == 1;
        
        for (int i = 1; i <= maxInt; i++) {
            unsigned maskCur = 1 << (i - 1);
            if ((maskUsed & maskCur) == 0) { // 当前数还未用过
                if (!dp(desired - i, maskUsed | maskCur)) {
                    memo[maskUsed] = 1;
                    return true;
                }
            }
        }
        memo[maskUsed] = -1;
        return false;
    };
    return dp(desired, 0);
} 

猜数字游戏,需要多少钱才能保证赢

int getMoneyAmount(int n) {
    // 设dp[i][j]表示猜[i..j]数字的子问题保证赢需要多少钱,1<=i<=j<=n
    // dp[i][j] = min{ k + max(dp[i][k-1], dp[k+1][j]) },i<=k<=j
    // 其中k+max(dp[i][k-1],dp[k+1][j])表示猜k保证赢需要多少钱,
    // dp[i][j] = min{...}表示所有保证赢的情况里最少需要多少钱。
    // 初始dp[i][i]=0

    vector<vector<int>> memo(n + 1, vector<int>(n + 1, INT_MAX));
    function<int(int,int)> dp = [&](int lo, int hi) {
        if (lo >= hi) return 0;
        if (memo[lo][hi] != INT_MAX) return memo[lo][hi];
        for (int k = lo; k <= hi; k++) {
            int money = k + max(dp(lo, k - 1), dp(k + 1, hi));
            memo[lo][hi] = min(memo[lo][hi], money);
        }
        return memo[lo][hi];
    };
    return dp(1, n);
}

只能从数组首尾取数,取得数的和较大的赢

站在playe1的角度,自己得分越大越好、对手得分越小越好,这里让对手得分为负,minmax简化成max算法。

bool PredictTheWinner(vector<int>& nums) {
    // 设dp[i][j]表示当前玩家从nums[i..j]局面能得的最高分,0<=i<=j<N
    // dp[i][j] = max(nums[i]-dp[i+1][j], nums[j]-dp[i][j-1])
    // 初始dp[i][i]=nums[i]
    // 省掉i这维,用临时变量ndp,i从右往左遍历,j从左往右遍历
    // ndp[j] = max(nums[i]-dp[j], nums[j]-ndp[j-1])
    const int N = nums.size();
    vector<int> dp(N);
    for (int i = N - 1; i >= 0; i--) {
        vector<int> ndp(N);
        ndp[i] = nums[i];
        for (int j = i + 1; j < N; j++) {
            ndp[j] = max(nums[i] - dp[j], nums[j] - ndp[j-1]);
        }
        swap(dp, ndp);
    }
    return dp[N-1] >= 0;
}