前言
新的一年,好好学习。
正文
问题来源
本问题来自leetcode上的486题。
问题描述
给定一个表示分数的非负整数数组。 玩家1从数组任意一端拿取一个分数,随后玩家2继续从剩余数组任意一端拿取分数,然后玩家1拿,……。每次一个玩家只能拿取一个分数,分数被拿取之后不再可取。直到没有剩余分数可取时游戏结束。最终获得分数总和最多的玩家获胜。
给定一个表示分数的数组,预测玩家1是否会成为赢家。你可以假设每个玩家的玩法都会使他的分数最大化。
示例 1:
示例 1:
输入: [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。
示例 2:
输入: [1, 5, 233, 7]
输出: True
解释: 玩家1一开始选择1。然后玩家2必须从5和7中进行选择。无论玩家2选择了哪个,玩家1都可以选择233。
最终,玩家1(234分)比玩家2(12分)获得更多的分数,所以返回 True,表示玩家1可以成为赢家。
分析:
我的想法是,采用回溯法。相同的一层是自身计算过程,则下一层则代表对手的计算过程。只要能将对手计算逼迫到输,则返回成功否则返回失败;下一层,也就是对手环节也会将自身的对手逼迫到输,若能则返回成功否则返回失败。
如下是我写的代码。
func PredictTheWinner(nums []int) bool {
return win(nums, 0, len(nums)-1, 0, 0) >= 0
}
func win(nums []int, left, right int, mycount, othercount int) int {
if left == right {
if mycount + nums[left] > othercount {
return 1
} else if mycount+nums[left] < othercount {
return -1
} else {
return 0
}
}
ret1 := win(nums, left+1, right, othercount, mycount+nums[left])
if ret1 == -1 {
return 1
}
ret2 := win(nums, left, right-1, othercount, mycount+nums[right])
if ret2 == -1 {
return 1
}
if ret1 == 0 || ret2 == 0 {
return 0
}
return -1
}
网上的递归解法
public class Solution {
public boolean PredictTheWinner(int[] nums) {
return helper(nums, 0, nums.length-1) >= 0;
}
public int helper(int[] nums, int start, int end) {
if(start == end) return nums[start];
else return Math.max(nums[start]-helper(nums, start+1,end), nums[end]-helper(nums, start,end-1));
}
}
网上动态规划解法
- 该问题没有直接比较一个选手所拿元素的和值,而是把问题转换为两个选手所拿元素的差值。这一点很巧妙,是关键的一步。
- 找出递推表达式:max(nums[beg] - partition(beg + 1, end), nums[end] - partition(beg, end + 1))
- 通过递推表达式构造递归算法是比较简单的。但是要构造一个非递归的算法难度较大。对于非递归算法,首先在dp中赋初始值,这是我们解题的第一步。在这个问题中,我们使用一个二位的数组dp来表示nums数组中任意开始和结束位置两人结果的差值。
初始的时候,我们仅仅知道对角线上的值。dp[i][i] = nums[i]. 这一点很好理解。public class Solution { public boolean PredictTheWinner(int[] nums) { if(nums == null || nums.length == 0) return false; int n = nums.length; int[][] dp = new int[n][n]; for(int i = 0; i < n; i++) { dp[i][i] = nums[i]; } for(int i = n-2; i >= 0; i--) { for(int j = i+1; j < n; j++) { dp[i][j] = Math.max(nums[i]-dp[i+1][j], nums[j]-dp[i][j-1]); } } return dp[0][n-1] >= 0; } }
总结:
勤思考。
结语
不管怎么样好好加油。