题目:
在 "100 game" 这个游戏中,两名玩家轮流选择从 1 到 10 的任意整数,累计整数和,先使得累计整数和 达到或超过 100 的玩家,即为胜者。
如果我们将游戏规则改为 “玩家 不能 重复使用整数” 呢?
例如,两个玩家可以轮流从公共整数池中抽取从 1 到 15 的整数(不放回),直到累计整数和 >= 100。
给定两个整数 maxChoosableInteger (整数池中可选择的最大数)和 desiredTotal(累计和),若先出手的玩家是否能稳赢则返回 true ,否则返回 false 。假设两位玩家游戏时都表现 最佳 。
算法:
方法一:模拟
执行会超时。因为desiredTotal最大20,考虑用state保存数字是否使用。二进制的第i位是否为1,代表i是否被使用了。
totalSum := maxChoosableInteger * (maxChoosableInteger + 1) / 2
if desiredTotal > totalSum {
return false
}
var dfs func(state int, sum int, maxInt int) bool
dfs = func(state int, sum int, maxInt int) bool {
for i := 1; i <= maxInt; i ++ {
if state & (1 << maxInt) > 0 {
continue
}
if sum + i >= desiredTotal {
return true
}
if !dfs(state & (1 << maxInt), sum + i, maxInt) {
return true
}
}
return false
}
return dfs(0, 0, maxChoosableInteger)
}
方法二:记忆化搜索
比如maxChoosableInteger为 5
选择1,2,3,再选择4
和选择2,1,3再选择4
的结果是一样的,1,2,3的先后选择次序不影响结果,可以把第一次计算了把结果存起来,这就是记忆化。
func canIWin(maxChoosableInteger int, desiredTotal int) bool {
totalSum := maxChoosableInteger * (maxChoosableInteger + 1) / 2
if desiredTotal > totalSum {
return false
}
// state最大的数字为1 << 21 - 1
visited := make([]int, 1 << 21)
var dfs func(state int, sum int, maxInt int) bool
dfs = func(state int, sum int, maxInt int) bool {
if visited[state] == 1 {
return true
}
if visited[state] == 2 {
return false
}
for i := 1; i <= maxInt; i ++ {
if state & (1 << i) > 0 {
continue
}
if sum + i >= desiredTotal {
visited[state] = 1
return true
}
if !dfs(state | (1 << i), sum + i, maxInt) {
visited[state] = 1
return true
}
}
visited[state] = 2
return false
}
return dfs(0, 0, maxChoosableInteger)
}