数组两端取数问题中的先手优势

问题描述

已知任意一个正整数的数组nums,它满足两个条件:

a. 元素的个数是偶数,

b. 所有数据的和为奇数

现在指定A,B两个人轮流从这个数组中取数,但是每个人只能从首或尾选择1个。两人依次拿完之后,各自将其手上所有的数字加起来,谁的和大谁取得胜利。

例子:

[5,3,4,5]

A先选择5,B也选择5,A再选择4,B再选择3。

最后A的总和是9,B的总和是8,因此A获得胜利

现在假设A,B都是理性人,以取得胜利为目标,请问这种游戏有没有先手优势,如果有的话,那么给定一个符合要求的数组,A手上的数总和最大可以领先B多少?

先附上代码:

#首先定义dp状态变量
nums = [4, 7, 2, 9, 5, 2] n = len(nums) dp = [[0 for _ in range(n)] for _ in range(n)] print(dp)
#初始话状态变量,代码这里写多了,考虑了nums数组为奇数的情况
if n%2==1: for i in range(n): dp[i][i] = nums[i] # print(dp[i][i]) # print(dp) lens = 2 else: for i in range(n - 1): dp[i][i + 1] = max(nums[i], nums[i + 1]) - min(nums[i] ,nums[i + 1]) lens = 3
#更新状态变量,当n等于lens-1 时,dp[0][lens-1] 就是我们需要求的结果了 while lens < n: for i in range(n - lens): dp[i][i + lens] = max(min(nums[i] - nums[i + 1] + dp[i + 2][i + lens],nums[i]-nums[i+lens]+dp[i+1][i+lens-1]), min(nums[i+lens]-nums[i+lens-1]+dp[i][i+lens-2],nums[i+lens]-nums[i]+dp[i+1][i+lens-1])) lens+= 2 print(sum(nums)+dp[0][n-1]/2,(sum(nums)-dp[0][n-1])/2)

#涉及到博弈论的动态规划问题,求解的时间空间复杂度均为o(n**2)

状态变量:

dp[i][i]:表示的是将子数组  nums[i:j + 1]交给AB两人选择最后的结果。

由于两人选择的结果一定是偶数,因此 j+1-i 也是偶数。 就是我们需要求的最终结果, dp[0][n-1] 代表数组的长度。

比如数组[4,7,2,9,5,2],假如数组只有从[2,9]这一小段的话话,那我们的最终结果很容易得出来,dp[2][3]=7。同理dp[1][2]=5,dp[3][4]=4

问题分析:

当每个人取完一个数字之后,剩余的数组还是偶数的,如果我们可以把在剩余数组中取数的最优解求解出来,那么加上之前选取的2个数字之差,就是我们需要求得的最优解。于是,很容易想到可以采用动态规划的方法来倒推,用一个状态变量来表示剩余数组的最优解。因此我们可以采用如下的方法定义状态变量。

状态转移方程:

当 j-i 等于1的时候,那么dp[i][j] 就自动等于abs(nums[i]-nums[j])了。如果j-i 等于3时,有以下四种方法可以达到这种状态:

 

从上面可以看到,为了达到  这个dp[i][j]状态,A有2种选择,针对A的每种选择B也有2种选择。

 

假如 A选择了i ,那么B 势必要从可以达到状态dp[i][j]  的两种方法种选择对自己更有利的方法:

min(dp[i+1][i+2]+nums[i]-nums[j], dp[i+2][j]+nums[i]-nums[i+1])

当A 选择的是j 是那么同样 B还是会选择对自己最有利的方法:

min(dp[i][i+1]+nums[j]-nums[i+2], dp[i+1][i+2]+nums[j]-nums[i])

当然 A也知道 B可能应对的方法,因此他也会选择对于自己最有利的方法,于是:

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

同理,去除j-i 等于3这个条件,对于任意 j-i-3=2k(k>=1)来说,状态转移方程为:

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

 

posted @ 2021-03-26 11:16  momomoi  阅读(318)  评论(0)    收藏  举报