前言
- 这道题目可谓是我比较难理解的题目了,因为我一直想不通为啥,直到看到了视频,自己敲击了一遍才理解。
- 题目网址:https://leetcode-cn.com/problems/predict-the-winner/
方法一:递归方法
- 可以说这个方法是直接了当的模拟每个玩家的最优解,如图所示
- 所以根据上图可以大致分为三种情况
- 第一种只剩下一个值给你
- 第二种只剩下两个值给你选,那就选其中最大一个
- 第三种数组还有很多值,给前后两个值给你选,那就要分不同的情况,需要将两个取值的结果进行递归
- 我们详细来说一下第三种情况,假设数组就是 1,5,233,7,再定义两个指针 l 和 r 分别指向1和7
- 当你选择了 l 的时候 ,玩家二就只能选择 l+1 , r 这两个数
- 如果他选择了 l + 1 ,那你只能选择 l+2 ,r 而这种时候就可以触发第二种情况
- 如果他选择了r , 那你只能选择 l+1 , r-1 而这也可以触发第二种情况
- 当你选择了 l 的时候 ,玩家二就只能选择 l+1 , r 这两个数
- 不知道你们发现没有,上面就已经形成了递归,根据这种思想就会形成下面的代码
class Solution {
public boolean PredictTheWinner(int[] nums) {
if(nums.length <= 1){
return true;
}
int sum = 0;
for(int i : nums){
sum += i;
}
int s = maxSource(nums,0,nums.length -1);
System.out.println(s);
return s>=(sum-s);
}
public int maxSource(int[] arr, int l,int r){
if(l==r){
return arr[l];
}
int left = 0;
int right = 0;
if(r-l == 1){
left = arr[l];
right = arr[r];
}
if(r-l >= 2){
left = arr[l] + Math.min(maxSource(arr,l+2,r), maxSource(arr,l+1,r-1));
right = arr[r] + Math.min(maxSource(arr,l+1,r-1), maxSource(arr,l,r-2));
}
return Math.max(left,right);
}
}
方法二:动态规划
-
动态规划就是将递归的方法使用二维数组来模拟,并且之前模拟的最优解可以用二维数组来存贮
-
首先创建一个二维数组,然后其行数和列数都等于数组的长度,dp[i][j] 表示当数组剩下的部分为下标 ii 到下标 jj 时,即在下标范围 [i, j] 中,当前玩家与另一个玩家的分数之差的最大值,注意当前玩家不一定是先手。
-
只有当 i≤j 时,数组剩下的部分才有意义,因此当 i>j 时,dp[i][j]=0。
-
当 i=j时,只剩一个数字,当前玩家只能拿取这个数字,因此对于所有 0≤i<nums.length,都有 dp[i][i]=nums[i]
-
如下图所示
-
当 i<ji<j 时,当前玩家可以选择nums[i] 或nums[j],然后轮到另一个玩家在数组剩下的部分选取数字。在两种方案中,当前玩家会选择最优的方案,使得自己的分数最大化。
-
所以可以推出来 dp[i][j] = Math.max(num[i] - dp[i+1][j], num[j] - dp[i][j-1]) 这个状态方程
-
就这样从后往前推,推出最后一个dp[0][3]看是否对于0,则可以看得出是否会赢。
class Solution {
public boolean PredictTheWinner(int[] nums) {
int length = nums.length;
int[][] dp = new int[length][length];
for(int i=0;i<length;i++){
dp[i][i] = nums[i];
}
for(int i=length-2;i>=0;i--){
for(int j=i+1;j<length;j++){
dp[i][j] = Math.max(nums[i] - dp[i+1][j], nums[j] - dp[i][j-1]);
}
}
return dp[0][length-1] >= 0;
}
}
总结
- 递归方法我懂了,但是不知道怎么说会比较清楚,所以看着很乱,动态规划本来是晕晕绕绕的,但是边写边想边画就理出来了。