leetcode 1140 石子游戏2
DFS
亚历克斯和李继续他们的石子游戏。许多堆石子 排成一行,每堆都有正整数颗石子 piles[i]。游戏以谁手中的石子最多来决出胜负。
亚历克斯和李轮流进行,亚历克斯先开始。最初,M = 1。
在每个玩家的回合中,该玩家可以拿走剩下的 前 X 堆的所有石子,其中 1 <= X <= 2M。然后,令 M = max(M, X)。
游戏一直持续到所有石子都被拿走。
假设亚历克斯和李都发挥出最佳水平,返回亚历克斯可以得到的最大数量的石头。
输入:piles = [2,7,9,4,4]
输出:10
解释:
如果亚历克斯在开始时拿走一堆石子,李拿走两堆,接着亚历克斯也拿走两堆。在这种情况下,亚历克斯可以拿到 2 + 4 + 4 = 10 颗石子。
如果亚历克斯在开始时拿走两堆石子,那么李就可以拿走剩下全部三堆石子。在这种情况下,亚历克斯可以拿到 2 + 7 = 9 颗石子。
所以我们返回更大的 10。
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/stone-game-ii
引用自leetcode刘岳大佬的解答和思路,觉得很巧妙
代码如下
以python为例:
def stoneGameII(self, piles: List[int]) -> int:
n = len(piles)
s = [0]*(n+1)
for i in range(n-1,-1,-1):
s[i] = s[i+1]+piles[i] #s[i]记录了第i堆到最后一堆石子的总数,这代表着某个人开始拿的时候
#可以拿的理论最大值(无论他能拿多少)
dp = {} #字典,用于记忆化搜索
def dfs(i,M): # i代表当前这个人拿到了第i堆,M代表当前的M值
if (i,M) in dp: #记忆化搜索,如果这个值已经被计算过,那么直接用这个值即可
return dp[(i,M)]
if i>=n: #i超过了总堆数,啥也拿不到
return 0
if 2*M>=(n-i): #这说明这个人有能力一次拿完,那么对于这个人来说为了最大化收益肯定全拿走
return s[i]
best = 0
for p in range(1,2*M+1):
best = max(best,s[i]-dfs(i+p,max(M,p))) #重点!对于每一种拿法进行遍历,拿0堆,1堆。。。
#2M堆。计算每一种可能性,并且和拿完之后的最优解
#进行比较
dp[(i,M)]=best #记录该次的值
return best
return dfs(0,1)
这一题我觉得有点像人工智能导论里面说的博弈树,拿石子的双方都在尽可能的使自己的利益最大化
所以不仅要考虑自己,也要考虑拿完之后别人会怎么拿。
best = max(best,s[i]-dfs(i+p,max(M,p)))
这一行的意义就在于,比如我从第i堆开始拿,拿了p堆。那么当前剩下的最大值就是s[i],那么对方肯定会从第i+p的位置开始拿,所以减去对方拿去的,剩下就是自己理论可以拿的最大值。
这样每次双方拿的时候都可以保证自己的利益最大化。
也许后续还可以通过α-β剪枝来加快速度