在问 · 算法题解

486. 预测赢家

题目描述

输入整数数组 nums。玩家 1 和玩家 2 用它玩游戏,两人初始分数都是 0,玩家 1 先行,双方轮流操作。

轮到某人时,他只能拿走当前数组最左端或最右端的一个数,这个数加到他的分数上并从数组中删除。数组被拿空时游戏结束。

两名玩家都会采取让自己得分最多的最优策略。若玩家 1 最终分数大于或等于玩家 2,则视为玩家 1 获胜,返回 true;否则返回 false。

示例 1
输入:nums = [1,5,2]
输出:false

解释:无论玩家 1 先拿 1 还是 2,玩家 2 都会拿走 5,玩家 1 得 3、玩家 2 得 5。

示例 2
输入:nums = [1,5,233,7]
输出:true

解释:玩家 1 先拿 1,之后无论对方拿 5 还是 7,玩家 1 都能拿到 233,以 234 比 12 获胜。

数据范围

  • 1 <= nums.length <= 20
  • 0 <= nums[i] <= 10^7

解题思路

定义 dp[i][j]:只剩 nums[i..j] 时,当前该取数的人能比对手多拿多少分。如果取左端,得到 nums[i],然后对手在 [i+1..j] 上比自己多 dp[i+1][j],净差为 nums[i] - dp[i+1][j];取右端同理。两者取大。按区间长度从小到大填表,最后看 dp[0][n-1] >= 0。

为什么正确

填 dp[i][j] 时,更短的区间 dp[i+1][j] 与 dp[i][j-1] 都已算好;“分差”的定义让双方共用同一张表。

复杂度分析

时间 O(n^2),空间 O(n^2)(可压缩到 O(n))。

易错点

不要用“每次拿两端较大的数”的贪心;[1,5,233,7] 中玩家 1 先拿较小的 1 才是最优。平局算玩家 1 赢,判断用 >= 0。

参考代码

Python、Java、C++、Go、JavaScript、TypeScript 六种语言,逻辑一致。

class Solution:
    def predictTheWinner(self, nums: List[int]) -> bool:
        n = len(nums)
        dp = [[0] * n for _ in range(n)]
        for i in range(n):
            dp[i][i] = nums[i]
        for length in range(2, n + 1):
            for i in range(n - length + 1):
                j = i + length - 1
                dp[i][j] = max(nums[i] - dp[i + 1][j], nums[j] - dp[i][j - 1])
        return dp[0][n - 1] >= 0

载入编辑器,自己跑一遍 →

动态规划 专题的其他题目

查看 动态规划 全部题目 →