石子游戏
亚历克斯和李用几堆石子在做游戏。偶数堆石子排成一行,每堆都有正整数颗石子 piles[i]
。
游戏以谁手中的石子最多来决出胜负。石子的总数是奇数,所以没有平局。
亚历克斯和李轮流进行,亚历克斯先开始。 每回合,玩家从行的开始或结束处取走整堆石头。 这种情况一直持续到没有更多的石子堆为止,此时手中石子最多的玩家获胜。
假设亚历克斯和李都发挥出最佳水平,当亚历克斯赢得比赛时返回 true
,当李赢得比赛时返回 false
。
示例:
输入:[5,3,4,5]
输出:true
解释:
亚历克斯先开始,只能拿前 5 颗或后 5 颗石子 。
假设他取了前 5 颗,这一行就变成了 [3,4,5] 。
如果李拿走前 3 颗,那么剩下的是 [4,5],亚历克斯拿走后 5 颗赢得 10 分。
如果李拿走后 5 颗,那么剩下的是 [3,4],亚历克斯拿走后 4 颗赢得 9 分。
这表明,取前 5 颗石子对亚历克斯来说是一个胜利的举动,所以我们返回 true 。
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/stone-game
解题思路
状态方程
考虑特殊情况,当只有两堆石子时,先手这选择最大那堆max(piles[0],piles[1])
,最终还是先手赢,返回true
。
当堆数n大于两堆时且n必须是偶数堆时,设dp[i][j]
为堆数下标i
到下标j
先手获得最大颗石子数,堆数下标从0开始。
- 当先手选择左边的石子堆时,当
piles[j] > piles[i+1]
时,有left = dp[i+1][j-1] + piles[i]
,否则left = dp[i+2][j] + piles[i]
- 当先手选择左边的石子堆时,当
piles[j-1] > piles[i]
时,有right= dp[i][j-2] + piles[j]
,否则right= dp[i+1][j-1] + piles[j]
最终取dp[i][j] = max(left,right)
。
初始化
当j-i=1
时,dp[i][j] = max(piles[i],piles[j])
返回值
dp[0][piles.length-1] > sum(piles) -dp[0][piles.length-1]
时先手赢,返回true
,否则返回false
代码实现
public class Solution {
public boolean stoneGame(int[] piles) {
int length = piles.length;
int[][] dp = new int[length][length];
for(int i = 1; i < length; i++){
dp[i-1][i] = Math.max(piles[i-1],piles[i]);
}
for(int step = 4; step <= length; step+=2){
for(int j = step - 1; j < length; j++){
int i = j - step + 1;
int temp1,temp2;
if(piles[j] > piles[i+1]){
temp1 = dp[i+1][j-1] + piles[i];
}else{
temp1 = dp[i+2][j] + piles[i];
}
if(piles[j-1] > piles[i]){
temp2 = dp[i][j-2] + piles[j];
}else {
temp2 = dp[i+1][j-1] + piles[j];
}
dp[i][j] = Math.max(temp1,temp2);
}
}
int sum = Arrays.stream(piles).sum();
int max = dp[0][piles.length -1];
return max > (sum - max);
}
public static void main(String[] args){
int[] nums = new int[]{6,9,4,3,9,8};
Solution solution = new Solution();
solution.stoneGame(nums);
}
}