题目描述
石头游戏1:亚历克斯和李用几堆石子在做游戏。偶数堆石子排成一行,每堆都有正整数颗石子 piles[i] 。
游戏以谁手中的石子最多来决出胜负。石子的总数是奇数,所以没有平局。
亚历克斯和李轮流进行,亚历克斯先开始。 每回合,玩家从行的开始或结束处取走整堆石头。 这种情况一直持续到没有更多的石子堆为止,此时手中石子最多的玩家获胜。
假设亚历克斯和李都发挥出最佳水平,当亚历克斯赢得比赛时返回 true ,当李赢得比赛时返回 false 。
分析:此类问题属于博弈问题,一般可用动态规划解决,难得是构造出好用的 dp Table 。
用一个数组 pilespiles 表示,piles[i] 表示第 i 堆石子有多少个。你们轮流拿石头,一次拿一堆,但是只能拿走最左边或者最右边的石头堆。所有石头被拿完后,谁拥有的石头多,谁获胜。
如果为堆数为偶数,那么谁先手谁会胜利。把问题一般化,石头的堆数可以是任意正整数,石头的总数也可以是任意正整数,这样就能打破先手必胜的局面了。
比如有三堆石头 piles = [1, 100, 3],先手不管拿 11 还是 33,能够决定胜负的 100100 都会被后手拿走,后手会获胜。
最终要求出先手和后手的最终分数差,谁得分多谁赢!
先看定义的dp 数组:
dp[i][j].fir:在留有 i,i+1,i+2,,,,,j 堆时,先手最多得的分,三维数组表示就是dp[i][j][0];
dp[i][j].sec:在留有 i,i+1,i+2,,,,,j 堆时,后手最多得的分,三维数组表示就是dp[i][j][1];
dp[i][j][0] = max(piles[i] + dp[i+1][j][1],piles[j] + dp[i][j-1][1]) #先手左边或右边的两种情况,取较大的结果;
if 先手拿左边的:
dp[i][j][1] = dp[i+1][j][0]
if 先手拿右边的:
dp[i][j][1] = dp[i][j-1][0]
base case :当 i = j 时,dp[i][j][0] = piles[i],dp[i][j][1] = 0
def stoneGame(piles):
n = len(piles)
if n == 1 :
return piles[0] > 0
dp = [[[0 for _ in range(2)] for _ in range(n) ]for i in range(n)]
#base case
for i in range(n):
dp[i][i][0] = piles[i]
dp[i][i][1] = 0
#顺序:从下到上,从左到右
for i in range(n-1,-1,-1):
for j in range(i+1 ,n):
#如果先手选择左边
left = piles[i] + dp[i+1][j][1]
right = piles[j] + dp[i][j-1][1]
if left < right:
dp[i][j][0] = right
dp[i][j][1] = dp[i][j-1][0]
else:
dp[i][j][0] = left
dp[i][j][1] = dp[i+1][j][0]
return dp[0][n-1][0] - dp[0][n-1][1] > 0
石头游戏3:Alice 和 Bob 用几堆石子在做游戏。几堆石子排成一行,每堆石子都对应一个得分,由数组 stoneValue 给出。
Alice 和 Bob 轮流取石子,Alice 总是先开始。在每个玩家的回合中,该玩家可以拿走剩下石子中的的前 1、2 或 3 堆石子 。比赛一直持续到所有石头都被拿走。
每个玩家的最终得分为他所拿到的每堆石子的对应得分之和。每个玩家的初始分数都是 0 。比赛的目标是决出最高分,得分最高的选手将会赢得比赛,比赛也可能会出现平局。
假设 Alice 和 Bob 都采取 最优策略 。如果 Alice 赢了就返回 “Alice” ,Bob 赢了就返回 “Bob”,平局(分数相同)返回 “Tie” 。
分析:每次能去一到三堆,谁取得多,谁赢!
dp[i]:只剩第i堆到最后一堆时,当前选手能拿到的最大分数,最后结果就是:dp[0] 与 总分数 - dp[0] 谁大谁赢!
base case : 只剩最后一堆时,dp[n-1] = piles[n-1]
为了让自己拿到较多的分,就得让对手少拿分,所以对手拿 min(dp[i+1],dp[i+2],dp[i+3]),
自己得到:dp[i] = 剩下所有分数 - min(dp[i+1],dp[i+2],dp[i+3])
状态方程转移顺序:从后往前
def stoneGame3(piles):
n = len(piles)
if n == 1 :
return "Alice"
dp = [0 for _ in range(n+3)]
dp[n-1] = piles[n-1]
for i in range(n-1,-1,-1):
score = sum(piles[i:n])
sec = min(dp[i+1],dp[i+2],dp[i+3])
dp[i] = score - sec
all_score = sum(piles)
if dp[0] < all_score - dp[0]:
return "Bob"
elif dp[0] > all_score - dp[0]:
return "Alice"
else:
return "Tie"
石头游戏2,先留着,不能直接说看了没看懂,看别人写的也没看懂,先留着它!涉及到博弈论的这些问题,多是用动态规划解决,难在 DP Table的定义,捷径不多,多做多练多想。
看到其他大佬的推荐博弈论的两篇文章,还没来得及看,先插眼!
博弈类入门:https://blog.csdn.net/acm_cxlove/article/details/7854530
博弈类进阶:https://blog.csdn.net/acm_cxlove/article/details/7854526