1、问题描述
给定一个数组nums,a、b两人轮流从数组的左端或右端取一个数作为自己的得分,假设两人足够聪明,都采用最优的策略取数,且a先取,问a能能拿到的最大的分数是多少? 示例:
输入:nums=[4,7,5,3] 输出:10 解释:a能拿到的最大分数为7+3=10.
2、解题思路
分析:由题意,我们可以明确以下几点: (1) 当选手、在子数组中取数时,无论怎么取,、最终的得分之和总是一个固定值,这个固定值等于子数组的所有元素之和; (2) 假设表示选手在子数组取数时的最优解,由于选手每次也是选择最优的解,所以或者就代表了选手在子数组取数时的最优解(因为选手取了一个数后剩下的元素要么是要么是)。 (3) 考虑到和的得分之和固定,即的得分加上的得分等于固定值,若想让选手的得分最高,则等价于让的得分最低。
根据以上结论,我们采用动态规划来解决该问题: (1)定义状态:
:选手在子数组上取数时的最大得分;
(2)状态转移: 选手在子数组的得分等于子数组之和减去选手子数组的得分,而选手要想得分最高,等价于选手的得分最低。
(3)确定起始: 当只剩下一个数时,取走它。 。 (4)确定终止 选手在整个数组取数时的最大得分。
注意,由于涉及到区间数组和的问题,因此可以采用前缀和来进行优化。
3、代码实现
int maxScore(vector<int> nums){
int len = nums.size();
//求数组的前缀和数组
vector<int> sums(len,0);
for(int i = 0; i < len; i++){
if(i > 0){
sums[i] = sums[i-1] + nums[i]
}
else{
sums[i] = nums[i];
}
}
//动态规划
vector<vector<int>> dp(len, vector<int>(len,0));
for(int i = 0; i < len; i++){
for(int j=i; j >=0; j--){
if(j == i){
dp[i][i] = nums[i];
}
else{
int scoresum = j > 0 ? sum[i] - sum[j-1] : sum[i];
dp[j][i] = scoresum - min(dp[j+1][i],dp[j][i-1];
}
}
}
return dp[0][len -1];
}