预测赢家

重新做一遍,两个月前做的已经忘记的差不多了。。。

给定一个表示分数的非负整数数组。 玩家 1 从数组任意一端拿取一个分数,随后玩家 2 继续从剩余数组任意一端拿取分数,然后玩家 1 拿,…… 。每次一个玩家只能拿取一个分数,分数被拿取之后不再可取。直到没有剩余分数可取时游戏结束。最终获得分数总和最多的玩家获胜。

给定一个表示分数的数组,预测玩家1是否会成为赢家。你可以假设每个玩家的玩法都会使他的分数最大化。

输入:[1, 5, 2]
输出:False
解释:一开始,玩家1可以从1和2中进行选择。
如果他选择 2(或者 1 ),那么玩家 2 可以从 1(或者 2 )和 5 中进行选择。如果玩家 2 选择了 5 ,那么玩家 1 则只剩下 1(或者 2 )可选。
所以,玩家 1 的最终分数为 1 + 2 = 3,而玩家 25 。
因此,玩家 1 永远不会成为赢家,返回 False 

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/predict-the-winner

这道题很有意思,因为这涉及到一点博弈的感觉,一开始接触总觉得无处下手。看完答案之后我只能说,妙极了。

这道题考虑的动态规划的求解,那么如何来定义base case,以及状态转移方程呢?

首先,先引入另一道题887.石子游戏,这是预测赢家的一个特例,它多了两个限制条件,1.数组长度为偶数 2.数组加和为奇数(一定有一个人赢)

这个题,只用一步就能求解,很有意思。

1 class Solution {
2 public:
3     bool stoneGame(vector<int>& piles) {
4         return true;
5     }
6 };

这里有些数学的东西,很好玩。

如图,4堆石子,分成两组,偶数为1,奇数为2.

玩家1先手,判断玩家1是不是能赢。首先玩家1可以选择1或者2,如果选择了1,则玩家2只能从2中选一个,接下来玩家1又能从1,2进行选择。

所以玩家1可以选择同一组,而玩家1完全可以通过计算哪一组的加和更大,选择哪一组,这样必然会胜利。

故,回到这一题,我们可以知道,当数组长度为偶数时 return true;

我们考虑奇数的情况

dp[i][j]表示子数组[i...j]中的最大得分(不区分玩家1和玩家2,因为每一位玩家都是想得到最大分数的)

那么对于数组只有一个元素时,dp[i][i]=num[i];

状态转移方程:dp[i][j],可以选择num[i],temp1=num[i]-dp[i+1][j]//减去玩家2的分数;选择num[j],temp2=num[j]-dp[i][j-1]

故dp[i][j]=max(temp1,temp2)

所以我们的代码为:

 1 class Solution {
 2 public:
 3     bool PredictTheWinner(vector<int>& nums) {
 4         //dp table
 5         int n=nums.size();
 6         if(n%2==0||n==0)
 7             return true;
 8         int dp[n][n];
 9         for(int i=0;i<n;i++)
10             dp[i][i]=nums[i];
11         for(int i=n-2;i>=0;i--){
12             for(int j=i+1;j<n;j++){
13                 dp[i][j]=max((nums[i]-dp[i+1][j]),(nums[j]-dp[i][j-1]));
14             }
15         }
16         return dp[0][n-1]>=0;
17     }
18 };
原文地址:https://www.cnblogs.com/doris233/p/13971758.html