从样例二可以看出, 用贪心算法,每次取左右两端的最大值,得到的不是最优解。
这里可以用递归搜索的思路来解决,状态函数的定义如下:
bool helper(int left, int right, int score_A, int score_B, bool turn_to_A, vector<int>& nums)
当搜索到最后score_A > score_B的时候就返回true。
显然A获胜的条件就是(要么向前拿获胜,要么向后拿获胜)
if(helper(left+1,right,score_A+nums[left],score_B,!turn_to_A,nums)||
helper(left,right-1,score_A+nums[right],score_B,!turn_to_A,nums)) return true;
对于B来说,不管B向前拿,向后拿都要获胜。注意这里的B考虑的不是局部最优,也是全局最优,所以这等价一个先手必胜的博弈问题。
class Solution {
public:
bool PredictTheWinner(vector<int>& nums) {
return helper(0,nums.size()-1,0,0,true,nums);
}
bool helper(int left, int right, int score_A, int score_B, bool turn_to_A, vector<int>& nums) {
if(left > right)
return score_A >= score_B;
if(turn_to_A){
if(helper(left+1,right,score_A+nums[left],score_B,!turn_to_A,nums)||
helper(left,right-1,score_A+nums[right],score_B,!turn_to_A,nums)) return true;
}else{
if(helper(left+1,right,score_A,score_B+nums[left],!turn_to_A,nums)&&helper(
left,right-1,score_A,score_B+nums[right],!turn_to_A,nums)) return true;
}
return false;
}
};
等价代换,A和B的局面分,可以用score_A - score_B代替,每一轮次,A想让局面分最大,B想让局面分最小,最后,如果存在路径使得局面分>0, 证明A存在必胜策略。
用局面分搜索的解法
class Solution {
public:
bool PredictTheWinner(vector<int>& nums) {
return maxScore(nums,0,nums.size()-1,1)>=0;
}
int maxScore(vector<int>& nums, int left, int right, int turn){
// turn = 1代表A得分,turn = -1代表B得分,返回值是当前状态得分的最大值
if(left==right) return nums[left]*turn;
int maxleft = nums[left]*turn + maxScore(nums,left+1,right,-turn);
int maxright = nums[right]*turn + maxScore(nums,left,right-1,-turn);
if(turn==1) return max(maxleft,maxright);
else return min(maxleft,maxright);
}
};
显然,以上解法为O(2^n)的时间复杂度,为了降低时间复杂度,考虑哪些搜素是重复的。
动态规划,自底向上求解。
dp[i][j]表示区间i到j,当前玩家与对手玩家分数差的最大值。
转移方程dp[i][j] = max( nums[i] - dp[i+1][j] , nums[j] - dp[i][j-1]])
j>=i+1
递推的时候是自底向上递推。
class Solution {
public:
// dp[i][j]表示区间为i到j时,当前玩家与对面玩家分数差的最大值
bool PredictTheWinner(vector<int>& nums) {
int n = nums.size();
vector<vector<int>> dp(n,vector<int>(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] = max(nums[i]-dp[i+1][j],nums[j]-dp[i][j-1]);
}
}
return dp[0][n-1]>=0;
}
};