
涉及game的问题,2个player,
现有几堆石头,每堆石头个数为piles[i],
刚开始M=1,
player1先拿石头,可以拿走前 x 堆(假设从第 i 堆开始拿,可以拿 i ~ i+x-1 堆),1 <= x <= 2M,
拿完之后,重置M=max(x, M),
然后player2开始拿,重复这个过程,直到所有的石头被拿完。
问player1最多可以拿多少个石头。
思路:
game理论就是min, max, 尽量做到自己max, 对手min。
枚举所有可能的情况,找到player1最多的石头。
假设从第 i 堆开始拿,
player1拿x堆,那么player1将会得到piles[i] + … + piles[i+x-1]个石头,
M更新为max(x, M),
那么下一次player2从 i+x 堆开始拿,最多拿 2*max(x,M) 堆。
直到石头被拿完。
要枚举所有的 1 <= x <= 2M,
同时M <= n (n为石头堆数),2M >= n-x时,可以一次性拿走剩下所有石头(x=2M时,可以拿完)。
方法1:DFS
用DFS模拟一次player1, player2交替拿石头的过程,
因为中间涉及到计算和的过程:piles[i] + … + piles[i+x-1],
提前计算积分数组pileSum,那么piles[i] + … + piles[i+x-1] = pileSum[i] - pileSum[i+x] (从右到左求和)
dfs(pileSum, i, m, dp)表示player从第 i 堆开始拿x堆石头(遍历所有x),m为当前的M,
保证调用它的player能石头最大化。
dp[i][m]保存从第i堆开始拿,限制为m的最大可拿石头数,可以避免重复计算。
因为最后求的是player1最多拿多少个石头,所以用player1的视角来调用DFS.
player1先拿,从第0堆开始拿,M=1。
所以调用dfs(pileSum, 0, 1, dp)
现在假设到了第 i 堆,
player1从第i堆开始拿走x堆石头后,会得到piles[i] + … + piles[i+x-1]个石头,
pileSum计算的是从右到左的和,因此piles[i] + … + piles[i+x-1] = pileSum[i]-pileSum[i+x]
M更新为max(x, M)
player1拿完之后,player2从i+x堆开始拿,限制为新的M=max(x, M),
根据game理论,对于player2来说,它自己也要石头最大化,
所以从player2的视角调用DFS, dfs(pileSum, i+x, max(x,M), dp)
player2拿完之后,player1可以拿pileSum[i+x] - player2拿的部分,
这就得到了player1最后拿的石头数。
用player1的视角调用DFS时,DFS函数的内部也有player2视角调用的DFS,
反之player2的视角调用DFS时,内部也有player1调用的DFS,
这就解释了game理论中的min, max, 最大化自己,最小化对手。
class Solution {
int n = 0;
public int stoneGameII(int[] piles) {
n = piles.length;
int[][] dp = new int[n][n

文章描述了一个两人游戏,玩家轮流从多堆石头中拿走一定数量的石头,目标是最大化自己的石头数量。提出了两种方法:深度优先搜索(DFS)和动态规划(DP)来解决此问题。在DFS中,通过预计算和数组优化了计算过程,而在DP中,自底向上地构建解决方案。两种方法都基于最大化玩家的优势并最小化对手的机会。
最低0.47元/天 解锁文章
526

被折叠的 条评论
为什么被折叠?



