python|博弈游戏01——拿纸牌
题目
描述 给定一个整型数组arr,代表数值不同的纸牌排成一条线。玩家A和玩家B依次拿走每张纸牌,规定玩家A先拿,玩家B后拿,但是每个玩家每次只能拿走最左或最右的纸牌,玩家A和玩家B都绝顶聪明。请返回最后获胜者的分数。
【举例】
arr=[1,2,100,4]。
开始时,玩家A只能拿走1或4。如果开始时玩家A拿走1,则排列变为[2,100,4],接下来玩家B可以拿走2或4,然后继续轮到玩家A...
如果开始时玩家A拿走4,则排列变为[1,2,100],接下来玩家B可以拿走1或100,然后继续轮到玩家A...
玩家A作为绝顶聪明的人不会先拿4,因为拿4之后,玩家B将拿走100。所以玩家A会先拿1,让排列变为[2,100,4],接下来玩家B不管怎么选,100都会被玩家A拿走。玩家A会获胜,分数为101。所以返回101。
arr=[1,100,2]。
开始时,玩家A不管拿1还是2,玩家B作为绝顶聪明的人,都会把100拿走。玩家B会获胜,分数为100。所以返回100。
解题思路 (1)递归问题为了方便进行理解,可以列举出递归的整个过程
(2)设输入的arr为[1, 8, 10, 6]
(3)用win函数去求arr的最优解法,即在先手和后手中取最优解
(4)其中0和3代表数组前值与后值的双指针
>>>win([1, 8, 10, 6], 4)
max(f([1, 8, 10, 6], 0, 3), s([1, 8, 10, 6], 0, 3))
(5)对于先手f函数,解析为取左边,或者取右边,使得自己结果最大,可以转换为,
>>>f([1, 8, 10, 6], 0, 3)
max(1 + s([1, 8, 10, 6], 1, 3), 6 + s([1, 8, 10, 6], 0, 2))
(6)对后后手s函数,解析为取左边,或者取右边,使得对手结果最小,可以转换为
>>>s([1, 8, 10, 6], 0, 3)
min(f([1, 8, 10, 6], 1, 3), f([1, 8, 10, 6], 0, 2))
(7)同理,1,3代表了取走了左边的牌,0,2代表取走了右边的牌
>>>1 + s([1, 8, 10, 6], 1, 3)
1 + min(f([1, 8, 10, 6], 2, 3), f([1, 8, 10, 6], 1, 2))>>>6 + s([1, 8, 10, 6], 0, 2)
6 + min(f([1, 8, 10, 6], 1, 2), f([1, 8, 10, 6], 0, 1))>>>f([1, 8, 10, 6], 1, 3)
max(8 + s([1, 8, 10, 6], 2, 3), 6 + s([1, 8, 10, 6], 1, 2))>>>f([1, 8, 10, 6], 0, 2)
max(1 + s([1, 8, 10, 6], 1, 2), 10 + s([1, 8, 10, 6], 0, 1))
(8)进一步往下进行列举
>>>f([1, 8, 10, 6], 2, 3)
max(10 + s([1, 8, 10, 6], 3, 3), 6 + s([1, 8, 10, 6], 2, 2))
max(10, 6)
10>>>f([1, 8, 10, 6], 1, 2)
max(8 + s([1, 8, 10, 6], 2, 2), 10 + s([1, 8, 10, 6], 1, 1))
max(8, 10)
10>>>f([1, 8, 10, 6], 1, 2)
max(8 + s([1, 8, 10, 6], 2, 2), 10 + s([1, 8, 10, 6], 1, 1))
max(8, 10)
10>>>f([1, 8, 10, 6], 0, 1)
max(1 + s([1, 8, 10, 6], 0, 0), 8 + s([1, 8, 10, 6], 1, 1))
max(1, 8)
8>>>s([1, 8, 10, 6], 2, 3)
min(f([1, 8, 10, 6], 3, 3), f([1, 8, 10, 6], 2, 2))
min(6, 10)
6>>>s([1, 8, 10, 6], 1, 2)
min(f([1, 8, 10, 6], 2, 2), f([1, 8, 10, 6], 1, 1))
min(10, 8)
8>>>s([1, 8, 10, 6], 1, 2)
min(f([1, 8, 10, 6], 2, 2), f([1, 8, 10, 6], 1, 1))
min(10, 8)
8>>>s([1, 8, 10, 6], 0, 1)
min(f([1, 8, 10, 6], 1, 1), f([1, 8, 10, 6], 0, 0))
min(8, 1)
1
(9)回到第(7)计算结果
>>> 1 + s([1, 8, 10, 6], 1, 3)
1 + min(f([1, 8, 10, 6], 2, 3), f([1, 8, 10, 6], 1, 2))
1 + min(10, 10)
11>>> 6 + s([1, 8, 10, 6], 0, 2)
6 + min(f([1, 8, 10, 6], 1, 2), f([1, 8, 10, 6], 0, 1))
6 + min(10, 8)
14>>> f([1, 8, 10, 6], 1, 3)
max(8 + s([1, 8, 10, 6], 2, 3), 6 + s([1, 8, 10, 6], 1, 2))
max(14, 14)
14>>> f([1, 8, 10, 6], 0, 2)
max(1 + s([1, 8, 10, 6], 1, 2), 10 + s([1, 8, 10, 6], 0, 1))
max(9, 11)
11
(10)回到第(5)步
>>>f([1, 8, 10, 6], 0, 3)
max(1 + s([1, 8, 10, 6], 1, 3), 6 + s([1, 8, 10, 6], 0, 2))
max(11, 14)
14
(11)回到第(6)步
>>>s([1, 8, 10, 6], 0, 3)
min(f([1, 8, 10, 6], 1, 3), f([1, 8, 10, 6], 0, 2))
min(14, 11)
11
【python|博弈游戏01——拿纸牌】(12)因此最优分数方案为先手拿牌,先取6再取8,分数为14,后手方案只能取10和1,分数为11
代码
def win(arr, sz):
if sz == 0:
return 0
else:
return max(f(arr, 0, sz - 1), s(arr, 0, sz - 1))#先手
def f(arr, i, j):
if i == j:
return arr[i]
else:
return max(arr[i] + s(arr, i + 1, j), arr[j] + s(arr, i, j - 1))#后手
def s(arr, i, j):
if i==j:#表示先手拿过了,只剩下一个的局面
return 0
else:
return min(f(arr, i + 1, j), f(arr, i, j - 1))#先手想赢,后手就需要拿最不好的if __name__=="__main__":
arr = [1, 8, 10, 6]
print(win(arr, len(arr)))
推荐阅读
- 游戏IP(立足于玩家情感的粉丝经济)
- python学习之|python学习之 实现QQ自动发送消息
- 逻辑回归的理解与python示例
- python自定义封装带颜色的logging模块
- 【Leetcode/Python】001-Two|【Leetcode/Python】001-Two Sum
- Python基础|Python基础 - 练习1
- Python爬虫|Python爬虫 --- 1.4 正则表达式(re库)
- 人生游戏--是游戏,还是人生()
- Python(pathlib模块)
- 「按键精灵安卓版」关于全分辨率脚本的一些理解(非游戏app)