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