LeetCode——石子游戏

261 阅读4分钟

「这是我参与11月更文挑战的第17天,活动详情查看:2021最后一次更文挑战

877 石子游戏

题目描述

亚历克斯和李用几堆石子在做游戏。偶数堆石子排成一行,每堆都有正整数颗石子 piles[i] 。

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

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

假设亚历克斯和李都发挥出最佳水平,当亚历克斯赢得比赛时返回 true ,当李赢得比赛时返回 false 。

示例:

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

方法一:动态规划

解题思路

dp[i][j]表示剩下第i堆到第j堆石子时,现在取的人能造成的最大石子差,其中i、j从0开始计数

初始化:

  1. dp[i][i] = piles[i]即仅剩最后一堆石子时能造成的最大石子差即为piles[i]
  2. dp[i][j] = 0,当i > j

状态转移方程

dp[i][j]=Math.max(piles[i]dp[i+1][j],piles[j]dp[i][j1])dp[i][j] = Math.max(piles[i] - dp[i + 1][j], piles[j] - dp[i][j - 1])

优化

考虑到dp[i][j]每的计算只与第i行和第i + 1行有关,所以不必使用二维数组,可以使用滚动数组,去掉dp的第一个维度

外层循环应从i最大值开始倒序遍历,保证转移来的是dp[i + 1][]的元素

代码

// 二维数组
/**
 * @param {number[]} piles
 * @return {boolean}
 */
var stoneGame = function(piles) {
    const n = piles.length
    const dp = new Array(n).fill(0).map(() => new Array(n).fill(0))
    for (let i = n - 1; i >= 0; i--) {
        dp[i][i] = piles[i]
        for (let j = i + 1; j < n; j++) {
            dp[i][j] = Math.max(piles[i] - dp[i + 1][j], piles[j] - dp[i][j - 1])
        }
    }
    if (dp[0][n - 1] > 0) {
        return true
    } else {
        return false
    }
};

// 一维数组
var stoneGame = function(piles) {
    const n = piles.length
    const dp = new Array(n).fill(0)
    for (let i = n - 1; i >= 0; i--) {
        dp[i] = piles[i]
        for (let j = i + 1; j < n; j++) {
            dp[j] = Math.max(piles[i] - dp[j], piles[j] - dp[j - 1])
        }
    }
    if (dp[n - 1] > 0) {
        return true
    } else {
        return false
    }
};

算法复杂度分析

  1. 时间复杂度:O(n * n)
  2. 空间复杂度:优化前O(n * n),优化后O(n)

方法二:数学推导

解题思路

  1. 将石子堆按照下标分成奇数堆和偶数堆两堆

  2. 对于一组连续的自然数i~j,自然数的个数可以决定头尾两个数奇偶性是否相同

    假如有偶数个,则j-i+1为偶数,那么j-i为奇数,所以ij奇偶性不同

    假如有奇数个,则j-i+1为奇数,那么j-i为偶数,所以ij奇偶性相同

  3. 对于一组连续的自然数i~j,假设有偶数个数,即ij奇偶性不同

    如果i为奇数,去掉i之后,i+1j均为偶数;去掉j之后,ij-1均为奇数

    如果i为偶数,去掉i之后,i+1j均为奇数;去掉j之后,ij-1均为偶数

  4. 由2可得,当石子剩下奇数堆数时,即第二个人取时,头尾下标奇偶性相同,即第二个人只能取奇数堆或偶数堆

    当石子剩下偶数堆数时,即第一个人取,头尾下标奇偶性不同,可以任意选择取奇数堆或偶数堆

  5. 由3可得,第一个人可以控制第二个人只取奇数堆或者偶数堆

  6. 由上可得,只要第一个人一直取偶数堆,那么第二个人只能取奇数堆

    或者第一个人一直取奇数堆,那么第二个人只能取偶数堆

    于是第一个人只要一直取总数多的那一堆则可以保证胜利

代码

/**
 * @param {number[]} piles
 * @return {boolean}
 */
var stoneGame = function(piles) {
	return true
};

算法复杂度分析

  1. 时间复杂度:O(1)
  2. 空间复杂度:O(1)