【每日力扣】石子游戏下的贪心博弈

这篇具有很好参考价值的文章主要介绍了【每日力扣】石子游戏下的贪心博弈。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

题目

石子游戏

Alice 和 Bob 轮流玩一个游戏,Alice 先手。

一堆石子里总共有 n 个石子,轮到某个玩家时,他可以 移出 一个石子并得到这个石子的价值。Alice 和 Bob 对石子价值有 不一样的的评判标准 。双方都知道对方的评判标准。

给你两个长度为 n 的整数数组 aliceValues 和 bobValues 。aliceValues[i] 和 bobValues[i] 分别表示 Alice 和 Bob 认为第 i 个石子的价值。

所有石子都被取完后,得分较高的人为胜者。如果两个玩家得分相同,那么为平局。两位玩家都会采用 最优策略 进行游戏。

请你推断游戏的结果,用如下的方式表示:

  • 如果 Alice 赢,返回 1 。
  • 如果 Bob 赢,返回 -1 。
  • 如果游戏平局,返回 0 。

示例 1:

输入:aliceValues = [1,3], bobValues = [2,1]
输出:1
解释:如果 Alice 拿石子 1 (下标从 0开始),那么 Alice 可以得到 3 分。Bob 只能选择石子 0 ,得到 2 分。Alice 获胜。

示例 2:

输入:aliceValues = [1,2], bobValues = [3,1]
输出:0
解释:Alice 拿石子 0 , Bob 拿石子 1 ,他们得分都为 1 分。打平。

示例 3:

输入:aliceValues = [2,4,3], bobValues = [1,6,7]
输出:-1
解释:不管 Alice 怎么操作,Bob 都可以得到比 Alice 更高的得分。比方说,Alice 拿石子 1 ,Bob 拿石子 2 , Alice 拿石子 0 ,Alice 会得到 6 分而 Bob 得分为 7 分。Bob 会获胜。

思路&code

何为最优策略?

        拿到这道题,首先可以提取出来两点关键信息:其一:双方都知道对方的评判标准。其二:两位玩家都会采用 最优策略 进行游戏。那么我们就要来想想看,所谓的最优策略到底是怎么样的呢?举例现在我的标准是[a,b,c],而我已知我的对手的标准是[d,e,f],如果我选择第一颗石子,那么毫无疑问我的得分会增加a,所以这里的最优策略是选择自己的最大值吗?很明显不是,因为这里存在一个博弈,游戏的本质是去争夺石子,也就是说你不仅要让自己获得高分,还要尽可能地去限制对手拿到高分。因此我们重新来看,假设不存在争夺,也就是双方都能拿到所有石子,那么毫无疑问,双方的分数就是sum(a)以及sum(b)。在争夺的语境下,我选择了第一颗石子,获得a分也就意味着我剥夺了对手获得他的第一颗石子所对应的d分的权利(可以理解为-d),那么其实可以理解为我选择第一颗石子,从而潜在获得了a+d分。因此我们可以看到,所谓的最优策略其实就是选择第i颗石子(a[i]+b[i]最大)。

class Solution:
    def stoneGameVI(self, a: List[int], b: List[int]) -> int:
        #最优选择(和最大:自己选大且不让别人选大)
        n = len(a)
        a_sorce,b_sorce = 0,0
        c = [x + y for x, y in zip(a, b)]
        for i in range(n):
            max_c = max(c)
            max_index = c.index(max_c)
            if i%2 == 0:a_sorce += a[max_index]
            else:b_sorce += b[max_index]
            c[max_index] = 0
        ans = a_sorce - b_sorce
        return 1 if a_sorce > b_sorce else (-1 if a_sorce < b_sorce else 0)

代码分析

        时间复杂度为O(n^2)

        n为列表长度(即有n颗石子待选)首先初始化ab两人的分数为0,接着将a与b相加得到我们所谓的“潜在博弈分数标准c”。接着通过n次循环,每次选出c中的最大值,得到它的索引值,因为我们并没有改变列表的顺序,所以得到的索引值就是ab中对应元素的索引值。a先选b后选,因此i为偶数时为a的得分(i初始为0),反之奇数时为b的得分。每次加上分数之后将c中对应元素更改为0,以进行下一个最大值的检索。最终分别得到ab分数,按照要求比较分数并返回对应数字。

算法优化

        可以看到算法时间复杂度过高,超出了时间限制。主要是在列表中找最大值的操作消耗了大量的时间,那么有什么办法可以优化代码,降低时间复杂度呢?

        说到寻找最大值消耗时间,我们很自然会想到通过排序来解决,但是在这里可以适用吗?重新查看代码,可以发现我们的代码是建立在列表顺序没有被更改的基础上的,如果进行排序操作,那么c中的元素序列将不再与ab两个列表对应,也就是说我们知道某一颗石子的“潜在博弈分数”是最大的,应该选这颗石子,但是我们并不知道这颗石子是哪一颗,进一步也就无从得知我能从这颗石子上获得的实际分数。

优化数据结构

        那么如果不用排序,我们就需要改变算法中的数据结构。这里我们引入双端队列这一数据结构来存储当前未被选择的元素的最大值及其索引。这样我们就可以实现在进行排序的同时保留原有的顺序序列,以进行后续的分数计算操作。但是双端队列比较复杂,不建议大家使用。

优化输赢判断

        在不改变数据结构的前提下,我们还是要用到排序,但是很明显排序与我们一开始构思的分数的计算方法相矛盾,那么我们现在要做的就是优化我们对输赢的判断方法,使其适应排序。我们现在的判断方法是分别得出两人的具体分数,再来比较以判断输赢。不妨思考一下,判断输赢是否真的需要得知两人的具体分数?还是回到我们最开始的思维方式,如果我不与对手争夺,也就是我一颗石子也不选,那么对手的分数应当是sum(b),现在我选择了第i颗石子,获得了a[i]分,同时剥夺了对手获得b[i]分的权利,那么其实我比对手多获取的分数就是我选择的潜在博弈分数总和 - 对手有机会获得的所有分数,而因为我是第一个选并且两人交替进行选择,所以我选择的潜在博弈分数总和其实就是排序之后序列的偶数项之和(sum(c[::2])),由此可以得出我比对手多获得的分数就是(sum(c[::2]) - sum(b))。这么说可能有点混乱,我们从数的角度来看,前者相当于是我选的获得的分数多加上了对应的对手获得的分数,而后者正好将这多加的部分多减掉了,最后得到了我选择的分数-对手选的分数。

class Solution:
    def stoneGameVI(self, a: List[int], b: List[int]) -> int:
        #最优选择(和最大:自己选大且不让别人选大)
        a_sorce,b_sorce = 0,0
        c = sorted([x + y for x, y in zip(a, b)],reverse=-1)
        ans = sum(c[::2]) - sum(b)
        return 1 if ans>0 else (-1 if ans < 0 else 0)

        时间复杂度为O(nlogn)文章来源地址https://www.toymoban.com/news/detail-833850.html

到了这里,关于【每日力扣】石子游戏下的贪心博弈的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 三种经典博弈(取石子问题)

    博弈是有一种很有意思的游戏,就是有物体若干堆,可以是火柴棍或是围棋子等等均可。两个人轮流从堆中取物体若干,规定最后取光物体者取胜。这是我国民间很古老的一个游戏,别看这游戏极其简单,却蕴含着深刻的数学原理。下面我们来分析一下要如何才能够取胜。

    2024年02月08日
    浏览(23)
  • 【力扣】55. 跳跃游戏 <贪心>

    给一个非负整数数组 nums ,最初位于数组的第一个下标 。数组中的每个元素代表你在该位置可以跳跃的最大长度。判断你是否能够到达最后一个下标,如果可以,返回 true ;否则,返回 false 。 示例 1: 输入:nums = [2,3,1,1,4] 输出:true 解释:可以先跳 1 步,从下标 0 到达下标

    2024年02月10日
    浏览(27)
  • 【动态规划】【C++算法】1563 石子游戏 V

    【数位dp】【动态规划】【状态压缩】【推荐】1012. 至少有 1 位重复的数字 动态规划汇总 几块石子 排成一行 ,每块石子都有一个关联值,关联值为整数,由数组 stoneValue 给出。 游戏中的每一轮:Alice 会将这行石子分成两个 非空行(即,左侧行和右侧行);Bob 负责计算每一

    2024年02月21日
    浏览(28)
  • leetcode292. Nim 游戏(博弈论 - java)

    难度 - 简单 原题链接 - Nim游戏 你和你的朋友,两个人一起玩 Nim 游戏: 桌子上有一堆石头。 你们轮流进行自己的回合, 你作为先手 。 每一回合,轮到的人拿掉 1 - 3 块石头。 拿掉最后一块石头的人就是获胜者。 假设你们每一步都是最优解。请编写一个函数,来判断你是否

    2024年02月12日
    浏览(40)
  • 【力扣·每日一题】2182.构造限制重复的字符串(模拟 贪心 优先队列 C++ Go)

    题目链接 给你一个字符串 s 和一个整数 repeatLimit ,用 s 中的字符构造一个新字符串 repeatLimitedString ,使任何字母 连续 出现的次数都不超过 repeatLimit 次。你不必使用 s 中的全部字符。 返回 字典序最大的 repeatLimitedString 。 如果在字符串 a 和 b 不同的第一个位置,字符串 a 中

    2024年01月17日
    浏览(41)
  • 力扣第55题 跳跃游戏 c++ 贪心 + 覆盖 加暴力超时参考

    55. 跳跃游戏 中等 相关标签 贪心   数组   动态规划 给你一个非负整数数组  nums  ,你最初位于数组的  第一个下标  。数组中的每个元素代表你在该位置可以跳跃的最大长度。 判断你是否能够到达最后一个下标,如果可以,返回  true  ;否则,返回  false  。 示例 1:

    2024年02月06日
    浏览(46)
  • (贪心) 1221. 分割平衡字符串 ——【Leetcode每日一题】

    难度:简单 平衡字符串 中, \\\'L\\\' 和 \\\'R\\\' 字符的数量是相同的。 给你一个平衡字符串 s ,请你将它分割成尽可能多的子字符串,并满足: 每个子字符串都是平衡字符串。 返回可以通过分割得到的平衡字符串的 最大数量 。 示例 1: 输入:s = “RLRRLLRLRL” 输出:4 解释:s 可以分

    2024年02月11日
    浏览(25)
  • leetcode55.跳跃游戏 【贪心】

    题目: 给你一个非负整数数组  nums  ,你最初位于数组的  第一个下标  。数组中的每个元素代表你在该位置可以跳跃的最大长度。 判断你是否能够到达最后一个下标,如果可以,返回  true  ;否则,返回  false  。 示例: 示例 1: 示例 2: 思路: 不能遍历依次遍历每

    2024年02月10日
    浏览(29)
  • 【力扣·每日一题】2645. 构造有效字符串的最小插入数(动态规划 贪心 滚动数组优化 C++ Go)

    题目链接 给你一个字符串 word ,你可以向其中任何位置插入 “a”、“b” 或 “c” 任意次,返回使 word 有效 需要插入的最少字母数。 如果字符串可以由 “abc” 串联多次得到,则认为该字符串 有效 。 提示: 1 = w o r d . l e n g t h = 50 1 = word.length = 50 1 = w or d . l e n g t h = 50 w

    2024年01月16日
    浏览(37)
  • (贪心) 剑指 Offer 14- II. 剪绳子 II ——【Leetcode每日一题】

    难度:中等 给你一根长度为 n 的绳子,请把绳子剪成整数长度的 m 段( m 、 n 都是整数, n 1 并且 m 1 ),每段绳子的长度记为 k[0],k[1]...k[m - 1] 。请问 k[0]*k[1]*...*k[m - 1] 可能的最大乘积是多少?例如,当绳子的长度是8时,我们把它剪成长度分别为2、3、3的三段,此时得到的最

    2024年02月13日
    浏览(35)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包