算法课第13周第1题——486. Predict the Winner

来源:互联网 发布:sql server 版本查看 编辑:程序博客网 时间:2024/06/08 03:05

题目描述:

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: FalseExplanation: 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: TrueExplanation: 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.
程序代码:

class Solution {public:bool PredictTheWinner(vector<int>& nums) {if (nums.empty())return false;// dp[i, j] 表示[i , j]之间玩家1 可以得到的最大数和。// 此时,因为两个玩家交替抽,所以dp[i + 1, j] 就会表示[i + 1 , j]之间玩家2 可以得到的最大数和。// 而相应地,此时玩家1 在dp[i + 1, j]之间可以得到的数和就为 sum[i, j] - dp[i + 1, j]// dp[i, j - 1]也是同理int n = nums.size();vector<int> temp(n, 0);vector<vector<int> > dp(n, temp);// 数组sum, sum[i]表示前i个数之和// [i, j]之间数的和sum[i, j] ,可以表示为sum[j] - sum[i - 1]  (是i-1 不是 i)vector<int> sum(n, 0);sum[0] = nums[0];for (int i = 1; i < n; i++) {sum[i] = sum[i - 1] + nums[i];}// 对于数组两端间隔长度0 ~ n-1作循环for (int len = 0; len <= n - 1; len++) {// 枚举0 ~ n-1中所有情况for (int i = 0; i + len < n; i++) {int j = i + len;// 若i == j, 需要特殊处理if (i == j) {dp[i][j] = nums[i];}// 若i+1 == j,也要特殊处理,因为此时数组两端间隔只有1,而sum[i, j]表示为sum[j] - sum[i - 1],数组两端间隔超过了1else if (i + 1 == j) {dp[i][j] = max(nums[i], nums[j]);}else {dp[i][j] = max(sum[j] - sum[i + 1 - 1] - dp[i + 1][j] + nums[i], sum[j - 1] - sum[i - 1] - dp[i][j - 1] + nums[j]);}}}// 判断玩家1的数字和是否更大if (dp[0][n - 1] >= sum[n - 1] - dp[0][n - 1])return true;else return false;}};



简要题解:

本题是运用动态规划解决一个博弈问题。

先理清题意。本题输入是一个数组,有两个玩家,从玩家1开始,轮流从数组两端选一个数给自己,直到数组中的数抽完,谁抽的数字之和大,谁获胜。最后需要判断玩家1是否能够取胜。

接着进行分析。若令dp[i, j]表示[i, j]间的数组中玩家1 可以得到的最大数和。那么因为双方玩家轮流抽,所以接下来就轮到玩家2. 此时玩家2有两种抽法,抽第i 或者第j个数字。假设玩家2抽第i 个数,那么接下来这轮玩家2可抽到的最大数和就能表示为dp[i + 1, j]。因为此时两个玩家抽到数之和为sum[i + 1, j] (即i+1到j之间数组所有数之和),所以,玩家1所抽到的数之和即为sum[i + 1,  j] - dp[i + 1, j] .  所以,可以推出当范围还在[i, j]时,玩家1抽到的数字和dp[i , j] = sum[i + 1, j] - dp[i + 1, j] + nums[i].

当玩家2抽第j个数时,也可以类似地分析。

此外,为了节约空间,可以将二维数组的sum[i, j]表示为一维数组sum[j] - sum[i - 1] , 这里的sum[j]表示前j个数之和。

所以我们就可以列出转移方程。两层循环,外层枚举len = 0 ~  n-1; 内层枚举 i = 0 循环到 i+len < n,且j = i + len:

dp[i , j]  = max { sum[i+1, j ] - dp[i+1, j] + nums[i],  sum[i, j - 1 ] - dp[i , j - 1] + nums[j] }

不过还有两个特殊的初始情况要处理,即 i == j和 i + 1 == j两种情况。当i == j:dp[i, j] = nums[i] ;  当i + 1 == j:dp[i, j] = max{nums[i], nums[j] }.

这样我们就列出了转移方程。而最后的输出结果,只要判断dp[0, n-1]是否比sum[n-1] - dp[0, n-1]更大(即玩家1的分数和是否比玩家2多)即可。


本题用动态规划解决博弈问题,在习题课上虽然也有提到,不过当时理解的不太透彻。这次通过自己亲身做题编程,对这个问题有了一个更深刻的理解。


原创粉丝点击