石子游戏 IV
难度:
标签:
题目描述
代码结果
运行时间: 121 ms, 内存: 19.9 MB
/*
* Alice 和 Bob 两个人轮流玩一个游戏,Alice 先手。
* 一开始,有 n 个石子堆在一起。每个人轮流操作,正在操作的玩家可以从石子堆里拿走 任意 非零 平方数 个石子。
* 如果石子堆里没有石子了,则无法操作的玩家输掉游戏。
* 给你正整数 n ,且已知两个人都采取最优策略。如果 Alice 会赢得比赛,那么返回 True ,否则返回 False 。
*/
import java.util.stream.IntStream;
public class StoneGameStream {
// 使用 Java Stream API 来解决这个问题
public boolean winnerSquareGame(int n) {
boolean[] dp = new boolean[n + 1];
IntStream.rangeClosed(1, n).forEach(i -> {
IntStream.iterate(1, k -> k * k <= i, k -> k + 1)
.filter(k -> !dp[i - k * k])
.findFirst()
.ifPresent(k -> dp[i] = true);
});
return dp[n];
}
public static void main(String[] args) {
StoneGameStream game = new StoneGameStream();
System.out.println(game.winnerSquareGame(1)); // true
System.out.println(game.winnerSquareGame(2)); // false
System.out.println(game.winnerSquareGame(4)); // true
System.out.println(game.winnerSquareGame(7)); // false
System.out.println(game.winnerSquareGame(17)); // false
}
}
解释
方法:
题解采用了记忆化搜索(动态规划的一种形式),其核心思想是递归地解决子问题,并缓存中间结果以避免重复计算。在每一步,Alice尝试拿走所有可能的平方数数量的石子。对于每一种可能的移动,Alice会检查剩余石子数量是否会导致对手Bob输掉游戏。如果存在任何一种移动使得Bob处于必败状态(即Bob无法通过任何方式赢得游戏),那么Alice就处于必胜状态。通过递归检查每一种可能的游戏状态,我们可以确定Alice是否有必胜的策略。
时间复杂度:
O(n * sqrt(n))
空间复杂度:
O(n)
代码细节讲解
🦆
为什么在递归函数中选择从最大的平方数开始尝试,而不是从最小的平方数开始?
▷🦆
在记忆化搜索中,如何确保对每个可能的游戏状态只计算一次?
▷🦆
在动态规划的过程中,如何处理递归深度过大导致的栈溢出问题?
▷🦆
在确定 Alice 是否有必胜策略时,算法是如何确保考虑了所有可能的策略的?
▷