LeetCode 486 Predict the Winner(预测赢家)

公開日: 2025-03-22 18:07 1424文字 8 min read

前端项目通过bridge获取客户端资源,客户端直接返回response对象常用代码模板1——基础算法常用代码模板2——数据结构统计iOS工程代码行数高数概念、公式、定理Objective-C 语法 3Objective-C 语法 2Objective-C 语法 1UIViewController 的生命周期UITableview调用reload方法时抖动问题UILabel中文带行间距的处理,限制行数,计算高度等UIButton扩大点击范围以及关于响应者链条的思考UIApplicationSwiftUI基本控件iPhone6 Plus上面神秘的缝隙iPhone 刘海机型UI适配(X、Xs、Xs Max、Xr)iOS:如何在UITableView调用reloadData刷新结束后再同步执行后续操作iOS 截取整个 scrollview 图片iOS 关于 UITextField 的字数限制Objective-C 中禁止调用指定的方法objc源码分析-runtime-classObjective-C Type EncodingsObjective-C:为什么分类中不能直接添加属性OC优缺点以及常见bugruntime——运行时简单使用当对象接收到不能处理的消息时调用的方法浅谈iOS中的weak为 UIControl 实现线程安全的 Block 事件扩展:原理与实践OC单例宏iOS常用数据类型转换OC中nil 、NULL、 Nil 、NSNull的区别Description方法和NSLog函数Block in Objective-CiOS自动化埋点的实现iOS平台编译Ogre游戏引擎库iOS:特殊符号大全iOS 网络小结iOS 沙盒与 BundleiOS 框架学习-AsyncSocketNSString的各种处理Swift Module 如何被全局引用CocoaPods组件化——OC/Swift动静态库混用COCOAPODS技巧-创建私有仓库关于NSNotificationCenter数据结构与算法解析习题2.23数据结构与算法解析习题2.19数据结构与算法解析习题2.16数据结构与算法解析习题2.14数据结构与算法解析习题2.13数据结构与算法解析习题2.12数据结构与算法解析习题2.11:二分查找数据结构与算法解析习题2.10:霍纳法则(Horner's rule)数据结构与算法解析习题2.7数据结构与算法解析习题1.3数据结构与算法解析习题1.2数据结构与算法解析习题1.1LeetCode 486 Predict the Winner(预测赢家)LeetCode 398 随机数索引LeetCode 106 Construct Binary Tree from Inorder and Postorder Traversal(由中序和后序遍历建立二叉树)LeetCode 70 爬楼梯(青蛙跳台阶)LeetCode 8 String to Integer (atoi)LeetCode 6 ZigZag Conversion(Z字转换)LeetCode 5 Longest Palindromic Substring(最长回文字串)iOS脚本打包 ipa(.app转.ipa)《什么是数学 》习题 第一章 补充《什么是数学 》习题 第一章 2 数系的无限性 数学归纳法《什么是数学 》习题 第一章 1 整数的计算Vue 的一些指令和缩写
この投稿は「日本語」では表示できません。元の投稿を表示しています。
题目 给定一个表示分数的非负整数数组。 玩家 1 从数组任意一端拿取一个分数,随后玩家 2 继续从剩余数组任意一端拿取分数,然后玩家 1 拿,…… 。每次一个玩家只能拿取一个分数,分数被拿取之后不再可取。直到没有剩余分数可取时游戏结束。最终获得分数总和最多的玩家获胜。 给定一个表示分数的数组,预测玩

题目

给定一个表示分数的非负整数数组。 玩家 1 从数组任意一端拿取一个分数,随后玩家 2 继续从剩余数组任意一端拿取分数,然后玩家 1 拿,…… 。每次一个玩家只能拿取一个分数,分数被拿取之后不再可取。直到没有剩余分数可取时游戏结束。最终获得分数总和最多的玩家获胜。

给定一个表示分数的数组,预测玩家1是否会成为赢家。你可以假设每个玩家的玩法都会使他的分数最大化。

Given an array of scores that are non-negative integers. Player 1 picks one of the numbers from either end of the array followed by the player 2 and then player 1 and so on. Each time a player picks a number, that number will not be available for the next player. This continues until all the scores have been chosen. The player with the maximum score wins.

Given an array of scores, predict whether player 1 is the winner. You can assume each player plays to maximize his score.

示例 1:

输入:[1, 5, 2]
输出:False
解释:一开始,玩家1可以从1和2中进行选择。
如果他选择 2(或者 1 ),那么玩家 2 可以从 1(或者 2 )和 5 中进行选择。如果玩家 2 选择了 5 ,那么玩家 1 则只剩下 1(或者 2 )可选。
所以,玩家 1 的最终分数为 1 + 2 = 3,而玩家 2 为 5 。
因此,玩家 1 永远不会成为赢家,返回 False 。

示例 2:

输入:[1, 5, 233, 7]
输出:True
解释:玩家 1 一开始选择 1 。然后玩家 2 必须从 5 和 7 中进行选择。无论玩家 2 选择了哪个,玩家 1 都可以选择 233 。
     最终,玩家 1(234 分)比玩家 2(12 分)获得更多的分数,所以返回 True,表示玩家 1 可以成为赢家。

提示:

  • 1 <= 给定的数组长度 <= 20.
  • 数组里所有分数都为非负数且不会大于 10000000 。
  • 如果最终两个玩家的分数相等,那么玩家 1 仍为赢家。

解题

设:总分 = 先手得分 - 后手得分。

当所有分数都被拿走后,如果总分>=0,就是先手胜,反之就是后手胜利。

由于是两个人分别拿,所以需要 turn 的正负来表示先手还是后手,在所有计算分数的地方都乘以 turn,使得总分就是差值。

最直观的思路就是用递归做出来,设数组两头的下标为 startend

递归函数中分别选择一个下标,计算选择下标之后递归计算出数组剩下的分被选择后的总分。

下标的分加上剩下的总分共有两种情况,取最大值,就是当前递归函数的总分。

递归中止条件为 start == end, 这时候直接返回当前下标的值就好了。

递归解法

解:


class Solution {
public:
    bool PredictTheWinner(vector<int>& nums) {
        return total(nums, 0, nums.size() - 1, 1) >= 0;
    }

    int total(vector<int>& nums, int start, int end, int turn) {
        if (start == end) {
            return nums[start] * turn;
        }
        int scoreStart = nums[start] * turn + total(nums, start + 1, end, -turn);
        int scoreEnd = nums[end] * turn + total(nums, start, end - 1, -turn);
        return max(scoreStart * turn, scoreEnd * turn) * turn;
    }
};

时间复杂度:O(2^n),其中 n 是数组的长度。

空间复杂度:O(n),其中 n 是数组的长度。空间复杂度取决于递归使用的栈空间。

动态规划解法

使用递归,存在大量重复计算,因此时间复杂度很高。可以试用动归解决这个问题。

用一个二维数组,记录计算过的总分。

dp[i][j] 为当前玩家在数组中,下标 i 到下标 j 的分数之差的最大值,当前玩家不一定是先手玩家。

根据题意,i不可能大于j。

当i==j时,dp[i][j] = nums[i];

计算分数的之差的最大值(状态转移)的公式是:

dp[i][j] = max(nums[i] - dp[i + 1][j], nums[j] - dp[i][j - 1]);

注意状态转移的方向,保证依赖的值已经有解。

class Solution {
public:
    bool PredictTheWinner(vector<int>& nums) {
        int length = nums.size();
        auto dp = vector<vector<int>> (length, vector<int>(length));
        for (int i = 0; i < length; i++) {
            dp[i][i] = nums[i];
        }
        // - 2是因为i == length - 1,这个值在刚刚已经算过了,只有一个值,即dp[i][i]
        for (int i = length - 2; i >= 0; i--) {
            for (int j = i + 1; j < length; j++) {
                dp[i][j] = max(nums[i] - dp[i + 1][j], nums[j] - dp[i][j - 1]);
            }
        }
        return dp[0][length - 1] >= 0;
    }
};

经过观察,dp[i][j] 的值只和 dp[i+1][j]dp[i][j−1] 有关。

即在计算第 i 行的值时,只使用到 dp 的第 i 行和第 i+1 行的值,而 j=i+1 ,所以大于 i+1 后面的行,都用不到了,可以借给 j 用。

列也只在意 jj - 1 的值,所以就可以用一个数组来记录。

可以优化一下空间,把二维数组换成一维数组。

class Solution {
public:
    bool PredictTheWinner(vector<int>& nums) {
        int length = nums.size();
        auto dp = vector<int>(length);
        for (int i = 0; i < length; i++) {
            dp[i] = nums[i];
        }
        for (int i = length - 2; i >= 0; i--) {
            for (int j = i + 1; j < length; j++) {
                dp[j] = max(nums[i] - dp[j], nums[j] - dp[j - 1]);
            }
        }
        return dp[length - 1] >= 0;
    }
};

时间复杂度:O(n^2),其中 n 是数组的长度。需要计算每个子数组对应的 dp 的值,共有 n(n+1)/2 个子数组。

空间复杂度:O(n),其中 n 是数组的长度。空间复杂度取决于额外创建的数组 dp,如果不优化空间,则空间复杂度是 O(n^2),使用一维数组优化之后空间复杂度可以降至 O(n)

© 2024 - 2026 cos @cosine
Powered by theme astro-koharu · Inspired by Shoka