题目链接:1140. Stone Game II
看到 Assuming Alex and Lee play optimally, return the maximum number of stones Alex can get. 这句话以为是博弈论的题,没想到是一道DP。
首先定义状态 dp[i][M] 表示从piles[i]开始拿(也就是说当前剩下的piles为
i
,
i
+
1
,
…
,
n
i,i+1,\dots,n
i,i+1,…,n ),且
1
≤
X
≤
2
M
1\leq X \leq 2M
1≤X≤2M 时(X为可以拿走的堆数),当前玩家能够获得的最大的石块数。根据上述定义,答案就是 dp[0][1] 。
那么如何保证两个人都是最优决策呢?这里实际上枚举了两个人所有的情况,然后得到最优决策。如果直接深度优先搜索的话,应该会TLE,这里使用dp数组来记忆计算过的状态,避免了重复计算,大大减少了时间。
状态转移方程如下:
for (int x = 1;x <= 2*M;++x){
mini = min(mini, helper(piles, i+x, max(M, x)));
}
dp[i][M] = sum[i] - mini;
helper(piles, i, M)
用来计算dp[i][M],for循环枚举了当前玩家的所有取法,然后计算下一个玩家在当前取法下能够得到的最大石块数,然后我们最大中取最小,得到了dp[i][M]。
C++代码如下:
class Solution {
private:
vector<int> sum;
vector<vector<int>> dp;
public:
int stoneGameII(vector<int>& piles) {
int n = piles.size();
if (n == 0) return 0;
sum = vector<int>(n, 0);
sum[n-1] = piles[n-1];
for (int i = n-2;i >= 0;--i){
sum[i] = sum[i+1] + piles[i];
}
dp = vector<vector<int>> (n, vector<int>(n, 0));
return helper(piles, 0, 1);
}
int helper(vector<int>& piles, int i, int M){
if (i >= piles.size()) return 0;
if (2*M >= piles.size()-i) return sum[i];
if (dp[i][M] != 0) return dp[i][M];
int mini = INT_MAX;
for (int x = 1;x <= 2*M;++x){
mini = min(mini, helper(piles, i+x, max(M, x)));
}
dp[i][M] = sum[i] - mini;
return dp[i][M];
}
};
参考:
[Java] DP with memorization, easy to understand(with explanation)