Leetcode 486 预测赢家 (博弈问题搜索和动态规划解法)

从样例二可以看出, 用贪心算法,每次取左右两端的最大值,得到的不是最优解。

这里可以用递归搜索的思路来解决,状态函数的定义如下:

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;
    }
};

猜你喜欢

转载自blog.csdn.net/wwxy1995/article/details/108341130
今日推荐