LeetCode 486. Predict the Winner

16 篇文章 0 订阅
10 篇文章 0 订阅

题目:给出一个数组,玩家1和玩家2轮流从数组的两端无放回地抽取一个数字(玩家1先抽),当数组被抽完时计算两个玩家的数字总和,总和数值大者为胜者。现在给出一个数组,预测玩家1能否胜出。

例子:

Input: [1, 5, 2]
Output: False
Explanation: Initially, player 1 can choose between 1 and 2.
If he chooses 2 (or 1), then player 2 can choose from 1 (or 2) and 5. If player 2 chooses 5, then player 1 will be left with 1 (or 2).
So, final score of player 1 is 1 + 2 = 3, and player 2 is 5.
Hence, player 1 will never be the winner and you need to return False.

Input: [1, 5, 233, 7]
Output: True
Explanation: Player 1 first chooses 1. Then player 2 have to choose between 5 and 7. No matter which number player 2 choose, player 1 can choose 233.
Finally, player 1 has more score (234) than player 2 (12), so you need to return True representing player1 can win.

解题思路:用动态规划解决这个问题。而动态规划的要点是定义问题的子问题,那么下面我们来定义这个问题的子问题。

假设给定数组nums,下面介绍一些参数定义:

f(i,j):假设游戏取值范围是从数组索引 i 到索引 j, 则玩家1抽到的数值的最大总和为f(i,j)(i>j,可以将f理解成一个矩阵,但只会用到上半角);

range_sum(i,j):数组索引 i 到索引 j的数值总和(可以将range_sum理解成一个矩阵,但只会用到上半角);

思想:若要求f(i,j),即目前游戏取值范围是nums[i],nums[i+1],....,nums[j],玩家1要么选nums[i],要么选nums[j],当选择nums[i]时,其最大总和:

f(i,j)=nums[i]+range_sum(i+1,j)-f(i+1,j),

这个怎么理解呢?

当玩家1选择nums[i]后到玩家2选择,那么假设玩家2一定会用最优的抽取方法的到最大总和,这个总和相当于玩家1从nums的i+1位置到j位置开始游戏得到的最大总和,即玩家2抽取的数值总和为f(i+1,j),那么玩家1除了nums[i]外只能得到range_sum(i+1,j)-f(i+1,j);同理,当玩家1选择nums[j]时,抽取数值最大总和:

f(i,j)=nums[j]+range_sum(i,j-1)-f(i,j-1),

综上所述,可将子问题按如下方式定义:

f(i,j)=max{ nums[i]+range_sum(i+1,j)-f(i+1,j) , nums[j]+range_sum(i,j-1)-f(i,j-1) };

以上是用矩阵的计算的,空间复杂度为O(nums.size()*num.size()),可以进一步将空间复杂度优化成O(num.size()),实现代码是优化后的方法;

实现代码如下:

class Solution {
public:
    bool PredictTheWinner(vector<int>& nums) {
        int size = nums.size();
        vector<int> range_sum(nums),scores(nums);
		range_sum.push_back(0);
		scores.push_back(0);
        for(int i = size-1; i > 0; --i){
            for(int j = 0; j < i; ++j){
                int end=size-i+j;
                scores[j] = nums[end]+range_sum[j]-scores[j] > \
                nums[j] + range_sum[j+1] - scores[j+1]?\
                nums[end]+range_sum[j]-scores[j]:\
                nums[j] + range_sum[j+1] - scores[j+1];
                range_sum[j] = range_sum[j] + nums[end];
            }
        }
        if(scores[0]>=1.0*range_sum[0]/2) return true;
        return false;
    }
};



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值