leetcode 石子游戏

石子游戏一

亚历克斯和李用几堆石子在做游戏。偶数堆石子排成一行,每堆都有正整数颗石子 piles[i] 。

游戏以谁手中的石子最多来决出胜负。石子的总数是奇数,所以没有平局。

亚历克斯和李轮流进行,亚历克斯先开始。 每回合,玩家从行的开始或结束处取走整堆石头。 这种情况一直持续到没有更多的石子堆为止,此时手中石子最多的玩家获胜。

假设亚历克斯和李都发挥出最佳水平,当亚历克斯赢得比赛时返回 true ,当李赢得比赛时返回 false 。

思路
dp[i][j][0]表示从i到j堆先手所能取得的最大值,dp[i][j][1]表示后手取得的最大值,有转移方程如下:
dp[i][j][0]=max(piles[i]+dp[i+1][j][1],piles[j]+dp[i][j-1][1])
dp[i][j][1]=min(dp[i+1][j][1],dp[i][j-1][1]) 后手状态转移由先手决定

class Solution:
    def stoneGame(self, piles: List[int]) -> bool:
        n=len(piles)
        dp=[[[0,0] for i in range(n+1)] for j in range(n+1)]
        for i in range(n):
           dp[i][i][0]=piles[i]
           dp[i][i][1]=0
        for l in range(1,n):
            for i in range(0,n-l):
                j=i+l
                dp[i][j][0]=max(piles[i]+dp[i+1][j][1],piles[j]+dp[i][j-1][1])
                dp[i][j][1]=min(dp[i+1][j][0],dp[i][j-1][0])
        if dp[0][n-1][0]>dp[0][n-1][1]:
            return True
        else:
            return False

为什么这里dp要有两维状态i和j呢?而石子游戏三中的定义只需要一维,这应该与问题的定义有关。第一个问题中,由于从头和尾取,那么显然有个边界的限制,而问题三中是可以连续取的,那么我们只有一维状态即可。

石子游戏二

亚历克斯和李继续他们的石子游戏。许多堆石子 排成一行,每堆都有正整数颗石子 piles[i]。游戏以谁手中的石子最多来决出胜负。

亚历克斯和李轮流进行,亚历克斯先开始。最初,M = 1。

在每个玩家的回合中,该玩家可以拿走剩下的 前 X 堆的所有石子,其中 1 <= X <= 2M。然后,令 M = max(M, X)。

游戏一直持续到所有石子都被拿走。

假设亚历克斯和李都发挥出最佳水平,返回亚历克斯可以得到的最大数量的石头。

思路
现在先手可以取到的范围不是固定的,但还是如问题三一样是连续的一个序列。因此我们可以定义dp[i][j]表示第i给位置,最远达到j给位置时先手取得的最大值,sum[i]存储从piles[i]后缀和。那么
可知dp[i][j]=sum[i]-min(dp[i+p][max(p,j/2)],其中1 <=p and p<=(j-i)

class Solution:
    def stoneGameII(self, piles: List[int]) -> int:
        n=len(piles)
        su=0
        dp=[[0 for i in range(n+1)] for j in range(n+1)]
        for i in range(n-1,-1,-1):
            su+=piles[i]
            for m in range(1,n+1):   
                if i+2*m>=n:
                    dp[i][m]=su
                    continue
                for j in range(1,2*m+1):
                    if i+j<n:
                        dp[i][m]=max(dp[i][m],su-dp[i+j][max(j,m)])
        return dp[0][1]

石子游戏三

Alice 和 Bob 用几堆石子在做游戏。几堆石子排成一行,每堆石子都对应一个得分,由数组 stoneValue 给出。

Alice 和 Bob 轮流取石子,Alice 总是先开始。在每个玩家的回合中,该玩家可以拿走剩下石子中的的前 1、2 或 3 堆石子 。比赛一直持续到所有石头都被拿走。

每个玩家的最终得分为他所拿到的每堆石子的对应得分之和。每个玩家的初始分数都是 0 。比赛的目标是决出最高分,得分最高的选手将会赢得比赛,比赛也可能会出现平局。

假设 Alice 和 Bob 都采取 最优策略 。如果 Alice 赢了就返回 "Alice" ,Bob 赢了就返回 "Bob",平局(分数相同)返回 "Tie" 。

思路
当前状态下的先手再取完一次后的下一状态变成后手,同理最初的后手下一状态变成先手。我们用dp[i][0]表示第i堆石子时,先手可以取得的最大值,dp[i][1]表示此时后手可以取得的最大值。
那么有转移方程:dp[i][0]=max(dp[i+1][1]+sum(stoneValue[i:i+1]),dp[i+2][1]+sum(stoneValue[i:i+2],dp[i+3][1]+sum(stoneValue[i:i+3])),设dp[i][0]由第i+p个状态转移而来
那么dp[i][1]=dp[i+p][0]

代码如下

class Solution:
    def stoneGameIII(self, stoneValue: List[int]) -> str:
        n=len(stoneValue)
        dp=[[0,0] for i in range(n+3)]
        dp[n-1][0],dp[n-1][1]=stoneValue[n-1],0
        for i in range(n-2,-1,-1):
            mx,mx_p=-float('inf'),-1
            for k in range(1,4):
                if i+k>n:
                    break
                tmp=(dp[i+k][1] if i+k<n else 0)+sum(stoneValue[i:i+k])
                if tmp>mx:
                    mx=tmp
                    mx_p=k
            dp[i][0]=mx
            dp[i][1]=dp[i+mx_p][0] if i+mx_p<n else 0
        if dp[0][0]>dp[0][1]:
            return "Alice"
        elif dp[0][0]<dp[0][1]:
            return "Bob"
        elif dp[0][0]==dp[0][1]:
            return "Tie"

利用问题二的思路,我们可以将二维状态优化为一维。dp[i]表示第i个位置先手取得的最大值,那么dp[i]=sum[i]-min(dp[i+1],dp[i+2],dp[i+3])

for(int n = stoneValue.size(), i = n-1; i >= 0; i--) {
            dp[i] = -0x7FFFFFFE;
            sum += stoneValue[i];
            for(int j = 1; j <= 3; j++) {
                dp[i] = max(dp[i], sum - dp[i+j]);
            }
        }
        if(sum - dp[0] == dp[0]) {
            return "Tie";
        } else if(sum - dp[0] > dp[0]) {
            return "Bob";
        }
        return "Alice";

猜你喜欢

转载自www.cnblogs.com/flightless/p/12748062.html