Medium

题目描述

Alice 和 Bob 用几堆石子在做游戏。一共有偶数堆石子排成一行,每堆都有正整数颗石子,数目为 piles[i]

游戏以谁手中的石子最多来决出胜负。石子的总数是奇数,所以没有平局。

Alice 和 Bob 轮流进行,Alice 先开始。每回合,玩家从行的开始或结束处取走整堆石头。这种情况一直持续到没有更多的石子堆为止,此时手中石子最多的人获胜。

假设 Alice 和 Bob 都发挥出最佳水平,当 Alice 赢得比赛时返回 true,当 Bob 赢得比赛时返回 false

示例 1:

输入:piles = [5,3,4,5]
输出:true
解释:
Alice 先开始,只能拿前面的 5 颗或后面的 5 颗石子。
假设他取了前面的 5 颗,这样一行石子变成了 [3,4,5] 。
如果 Bob 拿走了 3,那么剩下的是 [4,5],Alice 拿走 5 赢得 10 分。
如果 Bob 拿走了后面的 5,那么剩下的是 [3,4],Alice 拿走 4 赢得 9 分。
这表明,取前面的 5 颗石子对 Alice 来说是一个胜利的举动,所以返回 true。

示例 2:

输入:piles = [3,7,2,3]
输出:true

提示:

  • 2 <= piles.length <= 500
  • piles.length 是偶数
  • 1 <= piles[i] <= 500
  • sum(piles[i]) 是奇数

解题思路

这道题有两种经典解法:

解法一:数学证明(推荐) 由于石子堆数是偶数,我们可以将所有堆按索引分为两组:偶数索引组和奇数索引组。Alice 作为先手,可以通过控制选择策略来确保自己总是能选到其中一组的所有石子。具体来说:

  • 如果 Alice 第一步选择第一堆(索引0),那么 Bob 只能选择索引1或最后一个索引的堆
  • 无论 Bob 怎么选,Alice 都可以继续选择偶数索引的堆
  • 类似地,如果 Alice 第一步选择最后一堆,她可以控制选择奇数索引的堆

由于总石子数是奇数,偶数组和奇数组的石子数不可能相等,Alice 可以选择石子数更多的那组,因此 Alice 必胜。

解法二:动态规划 定义 dp[i][j] 表示当剩余石子堆为 piles[i...j] 时,当前玩家与另一玩家的石子数差的最大值。状态转移方程为: dp[i][j] = max(piles[i] - dp[i+1][j], piles[j] - dp[i][j-1])

虽然动态规划解法更通用,但对于这道特定题目,数学解法更优雅。

代码实现

class Solution {
public:
    bool stoneGame(vector<int>& piles) {
        return true;
    }
};
class Solution:
    def stoneGame(self, piles: List[int]) -> bool:
        return True
public class Solution {
    public bool StoneGame(int[] piles) {
        return true;
    }
}
/**
 * @param {number[]} piles
 * @return {boolean}
 */
var stoneGame = function(piles) {
    return true;
};

复杂度分析

复杂度数学解法动态规划解法
时间复杂度O(1)O(n²)
空间复杂度O(1)O(n²)

数学解法基于题目的特殊条件(偶数堆、奇数总和),直接得出 Alice 必胜的结论,是最优解法。

相关题目