一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第15天,点击查看活动详情。
题目说明
给你一个整数数组 nums 。玩家 1 和玩家 2 基于这个数组设计了一个游戏。
玩家 1 和玩家 2 轮流进行自己的回合,玩家 1 先手。开始时,两个玩家的初始分值都是 0 。每一回合,玩家从数组的任意一端取一个数字(即,nums[0] 或 nums[nums.length - 1]),取到的数字将会从数组中移除(数组长度减 1 )。玩家选中的数字将会加到他的得分上。当数组中没有剩余数字可取时,游戏结束。
如果玩家 1 能成为赢家,返回 true 。如果两个玩家得分相等,同样认为玩家 1 是游戏的赢家,也返回 true 。你可以假设每个玩家的玩法都会使他的分数最大化。
示例 1:
输入:nums = [1,5,2] 输出:false 解释:一开始,玩家 1 可以从 1 和 2 中进行选择。 如果他选择 2(或者 1 ),那么玩家 2 可以从 1(或者 2 )和 5 中进行选择。如果玩家 2 选择了 5 ,那么玩家 1 则只剩下 1(或者 2 )可选。 所以,玩家 1 的最终分数为 1 + 2 = 3,而玩家 2 为 5 。 因此,玩家 1 永远不会成为赢家,返回 false 。。
记忆化递归
可以很容易看出来这是一道经典博弈题。
看完这道题,我的思路是需要好好想一想:游戏获胜的条件是,最终取得全部分数的总和最大。
为了达到这个条件,我作为玩家,应该尽量让我选到的大,对手选到的小,如果每次我和队友得分的差值都尽可能的大,那么最后获胜的一定是我。
具体步骤:
1. 先创建一个二维数组用来记录各种情况下的差值。
2. 每次选取区间两头的一个,若区间l == r,表示只剩最后一个,直接返回该数;若不等,则会返回——我选数 - 对手选数的差值的最大值。
3. 直接算整个区间的差值(!!!通过开始的二维数组保存中间结果,如果已保存数字则直接返回)
public boolean PredictTheWinner(int[] nums) {
if (nums.length <= 1) {
return true;
}
int n = nums.length;
return getScore(nums, 0, nums.length - 1, new int[n][n]) >= 0;
}
private int getScore(int[] nums, int l, int r, int[][] memo) {
if (l == r) {
memo[l][r] = nums[l];
return nums[l];
}
if (memo[l][r] != 0) {
return memo[l][r];
}
int max = Math.max(nums[l] - getScore(nums, l + 1, r, memo), nums[r] - getScore(nums, l, r - 1, memo));
memo[l][r] = max;
return max;
}
动态规划
思路:
这题比较奇特,设的
dp[i][j]是nums[i,j]的时候,双方间的分数最大差值,这个dp可能是先手的,也可能是后手的,所以有dp[i][j] = max(nums[i] - dp[i+1][j], nums[j] - dp[i][j-1]);
dp[0][nums.size()-1] >= 0 就是我们要的结果剩下的优化还可以用dp压缩一下,因为后面只依赖前一次的结果。
class Solution {
public boolean PredictTheWinner(int[] nums) {
int [][]dp=new int[nums.length][nums.length];
for(int len=1;len<=nums.length;len++){
for(int i=nums.length-len;i>=0;i--){
int j=i+len-1;
if(i==j){
dp[i][j]=nums[i];
}else{
dp[i][j]=Math.max(nums[i]-dp[i+1][j],nums[j]-dp[i][j-1]);
}
}
}
return dp[0][nums.length-1]>=0;
}
}
注意
我们在使用动态规划来解决这个问题的时候。可以用 dp(i, j) 表示当剩下的数为 nums[i .. j] 时,当前操作的选手(注意,不一定是先手)与另一位选手最多的分数差。
当前操作的选手可以选择 nums[i] 并留下 nums[i+1 .. j],或选择 nums[j] 并留下 nums[i .. j-1],因此状态转移方程为:
dp(i, j) = max(nums[i] - dp(i+1, j), nums[j] - dp(i, j-1)), dp(i,j)依赖前面的值,前面的值分别来自下面一行和左边一列,因此是往二维数组的右上方移动,直到移动到第零行,如果dp(0, n - 1) >= 0,那么先手必胜。 初始条件为dp(i, i) = nums[i]