题目内容
Alice 和 Bob 用几堆石子在做游戏。几堆石子排成一行,每堆石子都对应一个得分,由数组 stoneValue 给出。
Alice 和 Bob 轮流取石子,Alice 总是先开始。在每个玩家的回合中,该玩家可以拿走剩下石子中的的前 1、2 或 3 堆石子 。比赛一直持续到所有石头都被拿走。
每个玩家的最终得分为他所拿到的每堆石子的对应得分之和。每个玩家的初始分数都是 0 。比赛的目标是决出最高分,得分最高的选手将会赢得比赛,比赛也可能会出现平局。
假设 Alice 和 Bob 都采取 最优策略 。如果 Alice 赢了就返回 "Alice" ,Bob 赢了就返回 "Bob",平局(分数相同)返回 "Tie" 。
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/stone-game-iii
题目分析
这道题其实跟数组两端取数之和很类似,都是双方都选择自己的最优解,因此我们依然可以采用类似的动态规划来解题。
解题思路
由于双方都是最优的,如果我们从前往后分析,每次双方都有3种选择策略,很难知道自己选择某个策略之后对方的最优解,也就很难分析下去。因此我们需要按照石子从后往前的顺序分析,倒推出最开始的最优状态。
状态表示: 令dp[i]代表从第i个石子到最后一个石子组成的序列中,当前取石子的选手能获得的最大值。
由于从i到最后一个石子的总分是固定的,就是所有石子的得分之和,而两个选手的得分之和为石子的总得分,因此要想当前取石子得分最高就相当于取了石子之后对方的得分最低。而当前取石子有1、2、3三种取法,因此如果我们知道了这三种取法取了之后对方的最优解,那么我们选择对方最优解最小的那种取法即可。状态转移方程如下:
// sum[i]为第i个石子到最后一个石子的总得分
dp[i] = sum[i] - min(dp[i+1], dp[i+2], dp[i+3]);
有了状态转移方程,再注意一下边界条件即可。
AC代码
class Solution {
public:
string stoneGameIII(vector<int>& stoneValue) {
int n = stoneValue.size();
int dp[n];
// res[i] 表示从第i个石子到最后一个石子的得分之和
int res[n];
res[n-1] = stoneValue[n-1];
for(int i=n-2;i>=0;--i){
res[i] = res[i+1]+stoneValue[i];
}
//dp[n-1] = res[n-1];
for(int i=n-1;i>=0;--i){
int minv;
// 这里表示如果剩下的石子小于等于3
// 该选手可以一次把所有石子取完
// minv = 0就表示另一个选手没有石子可以取
if(i+3>=n) minv = 0;
else minv = INT_MAX;
if(i+1<n){
minv = min(minv, dp[i+1]);
}
if(i+2<n){
minv = min(minv, dp[i+2]);
}
if(i+3<n){
minv = min(minv, dp[i+3]);
}
dp[i] = res[i]-minv;
}
if(res[0]-dp[0]>dp[0]) return "Bob";
else if(res[0]-dp[0] == dp[0]) return "Tie";
else return "Alice";
}
};
原文地址:https://www.cnblogs.com/wenxuanh/p/12636746.html