LeetCode 294: Flip Game II (Game Theory + DFS Memo)
LeetCode 294Source: https://leetcode.com/problems/flip-game-ii/
English
This is an impartial game. A state is winning if there exists at least one move that makes the opponent face a losing state. Use DFS to try every "++" -> "--" move, and memoize each string state. If any child state is losing, current state is winning; otherwise it is losing.
class Solution {
public boolean canWin(String currentState) {
if (currentState == null || currentState.length() < 2) return false;
return dfs(currentState.toCharArray(), new HashMap<>());
}
private boolean dfs(char[] s, Map<String, Boolean> memo) {
String key = new String(s);
if (memo.containsKey(key)) return memo.get(key);
for (int i = 0; i + 1 < s.length; i++) {
if (s[i] == '+' && s[i + 1] == '+') {
s[i] = '-';
s[i + 1] = '-';
boolean opponentWins = dfs(s, memo);
s[i] = '+';
s[i + 1] = '+';
if (!opponentWins) {
memo.put(key, true);
return true;
}
}
}
memo.put(key, false);
return false;
}
}func canWin(currentState string) bool {
memo := map[string]bool{}
seen := map[string]bool{}
var dfs func([]byte) bool
dfs = func(s []byte) bool {
key := string(s)
if seen[key] {
return memo[key]
}
seen[key] = true
for i := 0; i+1 < len(s); i++ {
if s[i] == '+' && s[i+1] == '+' {
s[i], s[i+1] = '-', '-'
opp := dfs(s)
s[i], s[i+1] = '+', '+'
if !opp {
memo[key] = true
return true
}
}
}
memo[key] = false
return false
}
return dfs([]byte(currentState))
}class Solution {
public:
bool canWin(string currentState) {
unordered_map<string, bool> memo;
return dfs(currentState, memo);
}
private:
bool dfs(string& s, unordered_map<string, bool>& memo) {
if (memo.count(s)) return memo[s];
for (int i = 0; i + 1 < (int)s.size(); ++i) {
if (s[i] == '+' && s[i + 1] == '+') {
s[i] = s[i + 1] = '-';
bool opp = dfs(s, memo);
s[i] = s[i + 1] = '+';
if (!opp) return memo[s] = true;
}
}
return memo[s] = false;
}
};class Solution:
def canWin(self, currentState: str) -> bool:
memo = {}
def dfs(s: str) -> bool:
if s in memo:
return memo[s]
for i in range(len(s) - 1):
if s[i:i + 2] == "++":
nxt = s[:i] + "--" + s[i + 2:]
if not dfs(nxt):
memo[s] = True
return True
memo[s] = False
return False
return dfs(currentState)var canWin = function(currentState) {
const memo = new Map();
const dfs = (s) => {
if (memo.has(s)) return memo.get(s);
for (let i = 0; i + 1 < s.length; i++) {
if (s[i] === '+' && s[i + 1] === '+') {
const nxt = s.slice(0, i) + '--' + s.slice(i + 2);
if (!dfs(nxt)) {
memo.set(s, true);
return true;
}
}
}
memo.set(s, false);
return false;
};
return dfs(currentState);
};中文
这是一个典型的博弈搜索题。若当前状态能走到任意一个“对手必败态”,那当前就是必胜态。我们用 DFS 枚举每个 "++" -> "--" 的操作,并对字符串状态做记忆化,避免重复搜索。只要存在一个后继状态返回 false,当前就返回 true;否则返回 false。
class Solution {
public boolean canWin(String currentState) {
if (currentState == null || currentState.length() < 2) return false;
return dfs(currentState.toCharArray(), new HashMap<>());
}
private boolean dfs(char[] s, Map<String, Boolean> memo) {
String key = new String(s);
if (memo.containsKey(key)) return memo.get(key);
for (int i = 0; i + 1 < s.length; i++) {
if (s[i] == '+' && s[i + 1] == '+') {
s[i] = '-';
s[i + 1] = '-';
boolean opponentWins = dfs(s, memo);
s[i] = '+';
s[i + 1] = '+';
if (!opponentWins) {
memo.put(key, true);
return true;
}
}
}
memo.put(key, false);
return false;
}
}func canWin(currentState string) bool {
memo := map[string]bool{}
seen := map[string]bool{}
var dfs func([]byte) bool
dfs = func(s []byte) bool {
key := string(s)
if seen[key] {
return memo[key]
}
seen[key] = true
for i := 0; i+1 < len(s); i++ {
if s[i] == '+' && s[i+1] == '+' {
s[i], s[i+1] = '-', '-'
opp := dfs(s)
s[i], s[i+1] = '+', '+'
if !opp {
memo[key] = true
return true
}
}
}
memo[key] = false
return false
}
return dfs([]byte(currentState))
}class Solution {
public:
bool canWin(string currentState) {
unordered_map<string, bool> memo;
return dfs(currentState, memo);
}
private:
bool dfs(string& s, unordered_map<string, bool>& memo) {
if (memo.count(s)) return memo[s];
for (int i = 0; i + 1 < (int)s.size(); ++i) {
if (s[i] == '+' && s[i + 1] == '+') {
s[i] = s[i + 1] = '-';
bool opp = dfs(s, memo);
s[i] = s[i + 1] = '+';
if (!opp) return memo[s] = true;
}
}
return memo[s] = false;
}
};class Solution:
def canWin(self, currentState: str) -> bool:
memo = {}
def dfs(s: str) -> bool:
if s in memo:
return memo[s]
for i in range(len(s) - 1):
if s[i:i + 2] == "++":
nxt = s[:i] + "--" + s[i + 2:]
if not dfs(nxt):
memo[s] = True
return True
memo[s] = False
return False
return dfs(currentState)var canWin = function(currentState) {
const memo = new Map();
const dfs = (s) => {
if (memo.has(s)) return memo.get(s);
for (let i = 0; i + 1 < s.length; i++) {
if (s[i] === '+' && s[i + 1] === '+') {
const nxt = s.slice(0, i) + '--' + s.slice(i + 2);
if (!dfs(nxt)) {
memo.set(s, true);
return true;
}
}
}
memo.set(s, false);
return false;
};
return dfs(currentState);
};
Comments