[DP]486. Predict the Winner

题目:

Given an array of scores that are non-negative integers. Player 1 picks one of the numbers from either end of the array followed by the player 2 and then player 1 and so on. Each time a player picks a number, that number will not be available for the next player. This continues until all the scores have been chosen. The player with the maximum score wins.

Given an array of scores, predict whether player 1 is the winner. You can assume each player plays to maximize his score.

Example 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.

Example 2:

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.

Note:

  1. 1 <= length of the array <= 20.
  2. Any scores in the given array are non-negative integers and will not exceed 10,000,000.
  3. If the scores of both players are equal, then player 1 is still the winner.

题目分析:
1、根据题意,题目假定两个玩家(A和B)对于给定的一个数列,每次只能从头和尾依次选出一个数,当把所有数选完过后比较两者所选数之和,和更大者胜出。当然两者在选择过程中都遵守都希望自己能赢得比赛的原则。我们要做的就是根据所给出的数列判断第一个选的人(A)是否能赢得比赛;
2、首先对题目的一些细节进行分析:
     1)每轮每个人各从两个可供选择中选出一个,因此每轮两人的选择组合一共有4种;
     2)当数组长度为1时,那么A直接选择这个数;当数组长度为2时,那么根据利益最大化,A选择两个数中更大者;
     3)所谓利益最大化原则并非贪心算法,而是要考虑到选择后的所有情况中的最优者;
3、很显然我们并不能直接给A指明每步该怎么去做,而是要把所有的情况例举出来选出最优解,这个时候无法使用传统的方法进行依次比较,因此我们需要对问题规模进行简化,即将这个问题变成规模更小的子问题。本题中考虑建立相邻两轮之间的关系,原因是相邻两轮对于选择者A来说都是一个独立的选择问题,而且两者之间的规模差距也比较好考虑,因为经过一轮选择数组大小总是减去2,且规模减小的方式也只有4种,此外终止条件也已经在上面列出,即数组大小为1或2时;
4、现在考虑具体的实现方法:
      对于选之前的vector v来说,每轮选择方式有四种:A、B都选头,A选头B选尾,A选尾B选头,A、B都选尾。那么依次可以得到四个子问题的容器v1、v2、v3、v4,现在假定函数Max(vector& v)就算出对于容器v的A开始选能够得到的最大的和Max(v),那么对于四种子情况他们的最优解便为Max(v1)、Max(v2)、Max(v3)、Max(v4)。那么现在就只需建立Max(v)和Max(vi)的关系了,显然假定v的头元素为begin,尾元素为end的话,Max(v)就是Max(v1)+ begin、Max(v2)+begin、Max(v3) + end、Max(v4)+ end中的一个。但是此时Max(v)并不是其中的最大的一个,因为作为玩家B他要最大化自己的利益,所以B要尽可能的在自己的选择范围内让A最小,即在A已经选择后(选头→v1、v2或尾→v3、v4),B要在选(头和尾)中选择让A最小的那种情况,即在v1、v2中选最小者,或者在v3、v4中选最小者。由此可以建立起Max(v)和Max(vi)间的关系:Max(v)= max(begin + min(Max(v1), Max(v2)), end + min(Max(v2), Max(v3)))。再结合前面所提到的唯一的两种终止条件, 可以构建出一个递归函数计算出Max(v)的值;
5、计算出Max(v)的值过后,只需要算出v中元素的总和,比较Max(v)是否过半来判断A是否获胜即可,这个过程中要注意尽量避免 /2 操作,避开有小数取整带来的多余考量。

代码:
#include 
#include 
#include 

int Max(vector& nums){
    if(nums.size() == 1)
        return nums[0];
    else if(nums.size() == 2)
        return max(nums[0], nums[1]);
    else{
        vector v1(nums);       // Situation 1:Player choose the first, playey 2 choose the second
        vector v2(nums);       // Situation 2/3: Player choose the first/end, playey 2 choose the end/first
        vector v3(nums);       // Situation 4: Player choose the end, playey 2 choose the second from the end
        
        int begin = nums[0];
        int end = nums[nums.size() - 1];
        
        v1.erase(v1.begin());       // for situation 1
        v1.erase(v1.begin());
        
        v2.erase(v2.begin());       // for situation 2 and 3
        v2.erase(v2.end() - 1);
        
        v3.erase(v3.end() - 1);      // for situation 4
        v3.erase(v3.end() - 1);
        
        return max(begin + min(Max(v1), Max(v2)), end + min(Max(v2), Max(v3)));
    }
}


class Solution {
public:
    bool PredictTheWinner(vector& nums) {
        int sum = 0;
        for(int i = 0; i < nums.size(); i++)
            sum += nums[i];
        int MaxA = Max(nums);
        return MaxA >= sum - MaxA;
    }
};  

你可能感兴趣的:([DP]486. Predict the Winner)