石子游戏III
题目:Alice和Bob用几堆石子在做游戏。几堆石子排成一行,每堆石子都对应一个得分,由数组stoneValue给出。
Alice和Bob轮流取石子,Alice总是先开始。在每个玩家的回合中,该玩家可以拿走剩下石子中的前1、2或3堆石子。比赛一直持续到所有石子被拿走。
每个玩家的最终得分为他所拿到的每堆石子的对应得分之和。每个玩家的初始分数都是0。比赛的目标是决出最高分,得分最高的选手将会赢得比赛,比赛也可能会出现平局。
假设Alice和Bob都采取最优策略。如果Alice赢了就返回“Alice”,Bob赢了就返回“Bob”,平局(分数相同)返回“Tie”。
思路:
比赛的时候,还是比较紧张的…前面3个一做完最后一题就紧张的不行。
很明显知道是动态规划,题干中重要的有两点:
- 玩家可拿走前1、2或3堆石子;
- Alice和Bob都采取最优策略;
即只有一堆石子时,玩家必须将其拿走。(我认为这是赢得比赛的关键)
最优策略也就是让自己最多,也就是让对方更少。
对手局面最差时,自己最优!
回忆:(这是当时比赛时的思路,留着想想哪里错了)
由于没想出来转移公式,比赛时用递归解的(能对才怪)
def digui(flag, lis, x, y)
当时的思路就是,用个flag表示谁拿(1代表Alice,0代表Bob),lis表示剩余的石头,x,y分别代表Alice和Bob的石子数目。
- 只剩一个石头时,当前玩家必须拿,判断x和y的值;
- 剩两个石头时:
a)当前玩家可以拿一个,那么下一个玩家必拿剩下的那个,判断x和y的值;
b)当前玩家两个都拿,判断x和y的值; - 剩三个石头时:
a)当前玩家拿一个,digui(flag^1, lis[1:], x+lis[0], y)
大概就是这个思路…
思路:
已经分析出来,当石子只剩一堆时,则玩家必拿。
所以动态方程从后向前推:
dp[i]代表从[i,…n]这些石子中,当前玩家最优策略得到的石子数。
转移方程:
- dp[i]=stoneValue[i]+ sum_stone - dp[i+1]
- dp[i]=stoneValue[i]+ sum_stone - dp[i+2]
- dp[i]=stoneValue[i]+ sum_stone - dp[i+3]
上面分别对应三种情况,当前玩家拿一堆、两堆和三堆石子。dp[i]取三者最优。
解释一下:此处dp[i+1]表示另一个玩家在[i+1,…n]堆石子最优策略得到的石子数,那么dp[i]应该为总的石子数减去对方石子数剩下的石子数。 (先后手交换)
class Solution(object):
def stoneGameIII(self, stoneValue):
"""
:type stoneValue: List[int]
:rtype: str
"""
l = len(stoneValue)
#考虑边界,列表长度应+3
dp = [0 for i in range(l+3)]
i = l-1
sum_stone = 0
while i >= 0:
dp[i] = float('-inf')
sum_stone += stoneValue[i]
for j in range(1,4):
dp[i] = max(dp[i], sum_stone - dp[i+j])
i -= 1
if dp[0] == sum_stone - dp[0]:
return 'Tie'
elif dp[0] > sum_stone - dp[0]:
return 'Alice'
return 'Bob'
附比赛成绩:比较满意的一周喔~