python二维游戏编程 最强大脑游戏_leetcode 第877题 石子游戏 python解法(用时420ms)...

leetcode 第877题 石子游戏 python解法(用时420ms)

该题是典型的双人回合制博弈游戏,两个人轮流从石子堆中拿出石子,其中石子都是按行一行行排好的,每个回合只能从头或者尾拿出一行石子。石子排列的行数是偶数,而且石子的总数是奇数,这样说明到最后不会出现平局。

问题分析

动态规划

这题本意是要求使用动态规划,动态规划最重要的是要写出状态转移方程。假设在某个状态时石子的排列为[2,4,7,1,8],数组中的每个数表示该行石子的总数。按照题目要求只能在头或尾取石子,所以本回合能取的只有2或8,而到底取哪一个,这是要进行比较才能做出选择。由于双方都不是傻子,所以每个回合选手选取的数都是对自己最有利的,换句话说,是对对方最不利的。

所以,这里我们选取其中某一个选手的角度展开问题,首先该选手会选取一个数作为自己的点数,所以是正值,接下来是对手的回合,他也会选取一个数,那么我们要将第一个数减去这个数,如果是整数,说明第一位选手取得多(赢了),如果是负数说明比较少(输了)。所以状态转移方程是 dp[i][j]=max(piles[i] - dp[i+1][j], piles[j] - dp[i][j-1]) 。状态方程要使用max()函数,这里涉及到另一个知识点即:极小化极大算法(Minimax,关于这部分大家可以上网查一查别的资料,很有趣)。前面说过,双方都不是傻子,所以每个回合都是结果最大的数,使用max()函数就能说明这点。这里i和j,指代的是一堆石子(这里用数组替换)中的第i行以及第j行。然后dp[i][j]存储的是从第i行到第j行选取后的最优结果。这个最优结果是选取首尾分别减去各自剩下的子数组的最优结构得到的。其中注意的是如果i=j,表示数组的长度为1,直接就可以返回piles[i]。

在程序开始之前,我们先要申请一个nxn(n是石子堆的长度)的二维数组。行代表的是开始的位置坐标,列是结束位置的坐标。所以在二维数组对角线上有i==j,所以可以直接将这些位置先赋值为piles[i](实际上用到的空间只有一半,大家可以想一想为什么)。

为了使这个过程更加直观一点,这里使用[2,5,233,7]做个例子,首先先生成一个二维数组,并在对角线上赋值。

接下来,开始从后向前遍历(从后向前遍历,每次使用的都是已经计算好的结果,所以可以用迭代。而从前向后遍历,每次的值都是下一步需要计算的,因此需要用到递归。使用递归虽然比较直观,好实现,但是比较慢。所以这里采用了迭代)。

计算的过程如下:

一直计算下去,最后计算的是dp[0][3],是指原数组的第一位到最后一位的最优解,它代表的是先手的选手得分减去后手的得分的结果,返回它是否大于零就好了。

逻辑判断

这一题其实非常简单,以至于评论里很多人都在调侃这道题。先从题目给的条件出发,题目中说石子排列的行数是偶数,所以抽象的数组的下标中奇数和偶数的个数一样。然后题目中又说石子的总数是奇数,那么最后所有行数为奇数的石子总数肯定不会等于偶数的石子总数-----要么大于,要么小于。最后题目又说必须在首或尾取石子,而且两位选手都发挥最佳水平。这样一来,只要先手的选手事先计算是行数为奇数的石子总数多,还是为偶数的总数最多,然后就可以根据规则,每个回合都可以确保自己能够取到奇数的行数或偶数的行数,立于不败之地。以piles=[2,3,233,56,9,7]这个来做为例子。先手的人通过计算可以得到,下标为偶数的数字总和相加要大于奇数的数字总和。所以开始先取piles[0]=2,下个回合轮到对手来取,这时候他只能选择piles[1]或pikes[5](下标都是奇数,取不到偶数)。无论对手取的是哪一个数,先手的人在下个回合总是可以取到下标为偶数的元素,并保证下下个回合对手还是取得奇数下标元素。到最后先手的人取倒的全是下标为偶数的元素,而对手取到的是奇数的,这样先手的人一定可以赢得比赛。

所以这道题偷懒的解法就是直接返回True就好了。

源码(Python)

class Solution:

def stoneGame(self, piles):

"""

:type piles: List[int]

:rtype: bool

"""

n = len(piles)

dp = [[piles[i] if i == j else 0 for i in range(n)] for j in range(n)]

for i in range(n-2, -1, -1):

for j in range(i+1, n):

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

return dp[0][n-1] > 0

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值