题目来源:力扣
题目介绍:
亚历克斯和李用几堆石子在做游戏。偶数堆石子排成一行,每堆都有正整数颗石子 piles[i] 。
游戏以谁手中的石子最多来决出胜负。石子的总数是奇数,所以没有平局。
亚历克斯和李轮流进行,亚历克斯先开始。 每回合,玩家从行的开始或结束处取走整堆石头。 这种情况一直持续到没有更多的石子堆为止,此时手中石子最多的玩家获胜。
假设亚历克斯和李都发挥出最佳水平,当亚历克斯赢得比赛时返回 true ,当李赢得比赛时
返回 false 。
由于题目中石子为偶数堆,因此在玩家均发挥最佳水平时,先手总是会赢得胜利.参照一份精彩的题解,我们将此题做修改,在任意整数堆情况下进行游戏,判断先手能否取得最终胜利.
审题:
-
确定状态
首先,我们考虑当前动态规划过程中的任一步的状态.对于该问题,很容易确定的一个状态是当前所剩石子.由于在每一步中用户仅能取第一堆或最后一堆,因此当前所剩的石堆是连续的,我们可以使用起始堆编号与结束堆编号描述当前的石堆状态.由于该问题为双人博弈游戏,因此另一游戏状态为当前正在走步的用户.我们可以使用三个状态变量描述当前游戏状态. -
确定状态转移方程
使用S[i][j][0]
表示在当前所剩石堆为石堆[i,j]时,正在走步的用户所能够获得的最多石子个数.S[i][j][1]
表示在当前所剩石堆为石堆[i, j]时,当前处于等待状态的用户所能获得的最多石子个数.我们可以推导出如下状态转移方程:
S[i][j][0] = Math.max(piles[i] + S[i+1][j][1], piles[j] + S[i][j-1][1])
//对于走步用户,其在下一状态为等待.
//等待用户在下一状态成为走步用户
if(走步用户选择了左侧堆)
S[i][j][1] = S[i+1][j][0];
else
S[i][j][1] = S[i][j-1][0];
- 确定基准状态
对于该问题,如果当前所剩石堆仅有一堆,则S[i][i][0] = piles[0], S[i][i][1] = 0;
走步用户取走该石堆,等待用户无石堆可取.
基于以上分析,S[0][piles.length-1][0]表示先手用户最终能获得的最多石子个数,S[0][piles.length-1][1]表示后手用户最终能获得的最多石子个数.
java代码
class Solution {
public boolean stoneGame(int[] piles) {
int[][][] S = new int[piles.length][piles.length][2];
//如果只剩一堆
for(int i = 0; i < piles.length; i++){
S[i][i][0] = piles[i];
S[i][i][1] = 0;
}
for(int len = 2; len <= piles.length; len++){
for(int l = 0; l <= piles.length-len; l++){
int r = l + len - 1;
//判断当前走步用户的两种不同选择
int left = piles[l] + S[l+1][r][1];
int right = piles[r] + S[l][r-1][1];
if(left > right){
S[l][r][0] = left;
S[l][r][1] = S[l+1][r][0];
}
else{
S[l][r][0] = right;
S[l][r][1] = S[l][r-1][0];
}
}
}
return S[0][piles.length-1][0] > S[0][piles.length-1][1];
}
}