题目:
Alice 和 Bob 玩一个游戏,两人轮流操作, Alice 先手 。
总共有 n 个石子排成一行。轮到某个玩家的回合时,如果石子的数目 大于 1 ,他将执行以下操作:
选择一个整数 x > 1 ,并且 移除 最左边的 x 个石子。
将 移除 的石子价值之 和 累加到该玩家的分数中。
将一个 新的石子 放在最左边,且新石子的值为被移除石子值之和。
当只剩下 一个 石子时,游戏结束。
Alice 和 Bob 的 分数之差 为 (Alice 的分数 - Bob 的分数) 。 Alice 的目标是 最大化 分数差,Bob 的目标是 最小化 分数差。
给你一个长度为 n 的整数数组 stones ,其中 stones[i] 是 从左边起 第 i 个石子的价值。请你返回在双方都采用 最优 策略的情况下,Alice 和 Bob 的 分数之差 。
示例 1:
输入:stones = [-1,2,-3,4,-5]
输出:5
解释:
- Alice 移除最左边的 4 个石子,得分增加 (-1) + 2 + (-3) + 4 = 2 ,并且将一个价值为 2 的石子放在最左边。stones = [2,-5] 。
- Bob 移除最左边的 2 个石子,得分增加 2 + (-5) = -3 ,并且将一个价值为 -3 的石子放在最左边。stones = [-3] 。
两者分数之差为 2 - (-3) = 5 。
示例 2:
输入:stones = [7,-6,5,10,5,-2,-6]
输出:13
解释:
- Alice 移除所有石子,得分增加 7 + (-6) + 5 + 10 + 5 + (-2) + (-6) = 13 ,并且将一个价值为 13 的石子放在最左边。stones = [13] 。
两者分数之差为 13 - 0 = 13 。
示例 3:
输入:stones = [-10,-12]
输出:-22
解释:
- Alice 只有一种操作,就是移除所有石子。得分增加 (-10) + (-12) = -22 ,并且将一个价值为 -22 的石子放在最左边。stones = [-22] 。
两者分数之差为 (-22) - 0 = -22 。
提示:
n == stones.length
2 <= n <= 105
-104 <= stones[i] <= 104
代码:
class Solution {
public:
int stoneGameVIII(vector<int>& stones) {
int n=stones.size();
vector<int> pre;
partial_sum(stones.begin(),stones.end(),back_inserter(pre));
vector<int> f(n);
f[n-1]=pre[n-1];
for(int i=n-2;i>=1;i--){
f[i]=max(f[i+1],pre[i]-f[i+1]);
}
return f[1];
}
};
思路:
Alice的目标是最大化分数差,Bob的目标是最小化分数差,那么简而言之就是他们都希望最大化自己的分数减去别人的分数,所以可以把他们统一在一个数组中进行动态规划;
结合题目的意思,应该可以明白,这是一个和前N项和有关的问题,就是A和B交替在前N项和的数组中按最优策略选择数字加在自己的分数上,每次选择的范围越来越小,后界是n-1,在这个过程中,求A先手的情况下,A和B的分数差;
使用f[i]表示下棋手在[i,n-1]的范围内选择的情况下的最大分数差,则动态规划转移公式为:
1)如果下棋手选择了i,那么棋手获得pre[i],下一步另一个棋手会选最优,所以此时f[i]=pre[i]-f[i+1];
2)如果下棋手没有选择i,他将在[i+1,n-1]的范围内选择,那么f[i]=f[i+1];
因为下棋手会选择最优策略,所以f[i]=max(f[i+1],pre[i]-f[i+1]);
最优要返回的是:f[1];因为根据题意,X需要大于1,所以不可以选择第一个石子;