













https://www.bilibili.com/video/BV15a4y1o7NA
有一个整数池,包含 \(1 \sim \text{maxChoosableInteger}\) ,每个数只能用一次。
两名玩家轮流取数,并把取到的数累加到公共和中。
谁先使累计和 \(\ge \text{desiredTotal}\) ,谁获胜。
假设双方都采取最优策略,判断 先手是否必胜。
这是一个典型的博弈 + 记忆化搜索(状态压缩 DP)问题。
否则是 必败态
由于:
👉 用一个 二进制 bitmask 表示状态:
1:数字 \(i+1\) 已被用过0:还没用例如(max=5):
mask = 01001(二进制)
表示:1 和 4 已经被使用
定义函数:
\[dfs(mask, sum) = \text{当前玩家在该状态下是否能必胜} \]
mask:已使用数字的集合sum:当前累计和对所有没被用过的数字 i:
sum + i >= desiredTotal
dfs(next_mask, sum + i)只要存在一种这样的选择,当前状态就是 必胜态
mask 的结果#include <bits/stdc++.h>
using namespace std;
/*
dp[mask]:
-1 表示尚未计算
0 表示当前状态必败
1 表示当前状态必胜
*/
int dp[1 << 20];
int maxN, target;
/*
dfs(mask, sum):
当前已使用数字集合为 mask,当前累计和为 sum
返回:当前玩家是否必胜
*/
bool dfs(int mask, int sum) {
// 如果已经算过,直接返回结果
if (dp[mask] != -1) return dp[mask];
// 尝试选择每一个还没用过的数字
for (int i = 1; i <= maxN; i++) {
int bit = 1 << (i - 1);
if (mask & bit) continue; // 这个数已经用过
// 如果选 i 可以直接达到目标,当前玩家立刻获胜
if (sum + i >= target) {
dp[mask] = 1;
return true;
}
// 否则,看对手在下一状态是否失败
if (!dfs(mask | bit, sum + i)) {
dp[mask] = 1; // 对手必败,当前必胜
return true;
}
}
// 所有选择都会让对手获胜,则当前必败
dp[mask] = 0;
return false;
}
bool canIWin(int maxChoosableInteger, int desiredTotal) {
maxN = maxChoosableInteger;
target = desiredTotal;
// 特判 1:目标为 0,先手直接赢
if (target <= 0) return true;
// 特判 2:所有数加起来都不够
int sum = maxN * (maxN + 1) / 2;
if (sum < target) return false;
// 初始化记忆数组
memset(dp, -1, sizeof(dp));
// 从初始状态开始:mask = 0,sum = 0
return dfs(0, 0);
}
\[O(2^n \cdot n) \]
\[O(2^n) \]
在题目限制内完全可行。
dfs记忆化只需记忆state这个可变参数,因为sum由state决定
class Solution {
public:
int memo[1<<20];// -1 没算过, 1 true , 0 false
int m_maxChoosableInteger, m_desiredTotal;
bool canIWin(int maxChoosableInteger, int desiredTotal) {
if((1+maxChoosableInteger)*maxChoosableInteger/2<desiredTotal) return false;//总和小于目标,谁都不能获胜
m_maxChoosableInteger=maxChoosableInteger, m_desiredTotal=desiredTotal;
memset(memo, -1, sizeof memo);
return dfs(0, 0);
}
//当前玩家在当前状态下,是否能获胜。 sum由state决定,所以不必缓存
bool dfs(int state, int sum){
if(memo[state]!=-1) return memo[state];//算过,直接返回
for(int i=0;i<m_maxChoosableInteger;i++){
if((state>>i)&1!=0) continue;// i+1用过了
if(sum+i+1>=m_desiredTotal) return memo[state]=true;
if(!dfs(state|(1<<i), sum+i+1)) return memo[state]=true;//选了i+1, 并且对手不能赢,我就赢了
}
return memo[state]=false;//遍历所有情况,我还不能赢
}
};
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。