本题出自力扣题库第877题。题面大意如下:
甲乙两人用几堆石子在做游戏。一共有偶数堆石子,排成一行;每堆都有正整数颗石子,数目为 piles[i] 。 游戏以谁手中的石子最多来决出胜负。石子的总数是奇数,所以没有平局。 甲乙轮流进行,甲先开始 。 每回合,玩家从行的开始或结束处取走整堆石头。 这种情况一直持续到没有更多的石子堆为止,此时手中石子最多的玩家获胜 。 假设甲乙都选择最佳策略,当甲赢时返回 true,当乙时返回 false 。
示例:
输入:piles = [5,3,4,5]
输出:true
解释:
甲先开始,取了前 5 颗,这一行就变成了 [3,4,5]。
如果乙拿走前 3 颗,那么剩下的是 [4,5],甲拿走后5颗赢得10分。
如果乙拿走后 5 颗,那么剩下的是 [3,4],甲拿走后4颗赢得9分。
这表明,取前5颗石子对甲来说是一个胜利的举动,所以返回 true 。
题解:
这个题目属于双人博弈类DP问题,对于此类问题使用递归法较为容易理解。所需要实现的递归方法就是符合游戏规定的最佳策略。根据题意,玩家在每一步都有两个选择,取第一堆,或是最后一堆,两者之间的最优选择就是当玩家取了一堆石头后,其获得的石头数与对家在下一步可能获得的石头数的差最大化。递归过程则代表双方轮流进行选择,递归的边界情况在于当只剩最后一堆石头时,那么当前玩家就只有一个选择以结束游戏,递归也因此结束。
Java代码如下:
class Solution {
int[][] dp;
public boolean stoneGame(int[] piles) {
int N = piles.length;
if (N <= 1) {
return true;
}
dp = new int[N][N];
return helper(0, N - 1, piles) > 0 ;
}
private int helper(int start, int end, int[] piles) {
if (end == start) {
return piles[start];
}
if (dp[start][end] != 0) {
return dp[start][end];
}
int optionA = piles[start] - helper(start + 1, end, piles);
int optionB = piles[end] - helper(start, end - 1, piles);
return dp[start][end] = Math.max(optionA, optionB);
}
}
本题也可以通过数学方法证明先手的甲总是会赢。