leetcode算法题--预测赢家★

原题链接:https://leetcode-cn.com/problems/predict-the-winner/

1、递归

递归方法,每次从头和尾各选一个,然后接着取下一个状态的小的那一个,因为对方肯定把大的取走了,然后最后的结果去最大,所以这题也是最大的最小,max(min()),不同于猜数字大小 II这题是最好的最坏方法,是min(max())

bool PredictTheWinner(vector<int>& nums) {
    int len=nums.size();
    int sum=0;
    for(auto x:nums) sum+=x;
    if(2*select(nums,0,len-1)>=sum) return true;
    else return false;
}

int select(vector<int>& nums,int start,int end){
    if(start>end) return 0;
    if(start==end) return nums[start];
    int x=nums[start]+min(select(nums,start+2,end),select(nums,start+1,end-1));
    int y=nums[end]+min(select(nums,start+1,end-1),select(nums,start,end-2));
    return max(x,y);
}

2、动态规划(自底向上)

动态规划的方法是比较巧妙

dp[i][j]保存从i到j领先一方领先的数

状态转移

dp[i][i]=nums[i]
dp[i][j]=max(nums[i]-dp[i+1][j],nums[j]-dp[i][j-1])

dp[i][i]代表只有这一个数,那么先手者肯定就直接拿走了。这里dp[i+1][j]和dp[i][j-1]都是表示上次另外一方的领先数,使用nums[i]-dp[i+1][j]或nums[j]-dp[i][j-1]就表示分别选头部和尾部之后,领先的数。于是,当len为奇数时正好是先手的轮次,若dp[0][len-1]>0,先手必赢。当len为偶数时,dp[0][len-1]一定大于0,先手必赢,原因见石子游戏。

至于双层循环的写法,如图(盗图):
leetcode算法题--预测赢家★_第1张图片
其实就是按填充顺序来写的循环!

 bool PredictTheWinner(vector<int>& nums) {
    int len=nums.size();
    vector<vector<int>> dp(len,vector<int>(len,0));
    for(int i=0;i<len;i++) dp[i][i]=nums[i];
    for(int i=len-1;i>=0;i--){
        for(int j=i+1;j<len;j++){
            dp[i][j]=max(nums[i]-dp[i+1][j],nums[j]-dp[i][j-1]);
        }
    }
    return dp[0][len-1]>=0;
}

3、动态规划(自顶向下)

还有种动态规划的写法,本质和上一种一样,就是改变了遍历的顺序,如图(盗图):
leetcode算法题--预测赢家★_第2张图片

bool PredictTheWinner(vector<int>& nums) {
   int len=nums.size();
   vector<vector<int>> dp(len,vector<int>(len,0));
   for(int i=0;i<len;i++) dp[i][i]=nums[i];
   for(int size=1;size<len;size++){
       for(int i=0;i<len-size;i++){
           int j=i+size;
           dp[i][j]=max(nums[i]-dp[i+1][j],nums[j]-dp[i][j-1]);
       }
   }
   return dp[0][len-1]>=0;
}

这种方法是不是和猜数字大小非常相似呢?

你可能感兴趣的:(算法)