https://leetcode.com/problems/stone-game-ii/
Alex and Lee continue their games with piles of stones. There are a number of piles arranged in a row, and each pile has a positive integer number of stones piles[i]
. The objective of the game is to end with the most stones.
Alex and Lee take turns, with Alex starting first. Initially, M = 1
.
On each player's turn, that player can take all the stones in the first X
remaining piles, where 1 <= X <= 2M
. Then, we set M = max(M, X)
.
The game continues until all the stones have been taken.
Assuming Alex and Lee play optimally, return the maximum number of stones Alex can get.
Example 1:
Input: piles = [2,7,9,4,4]
Output: 10
Explanation: If Alex takes one pile at the beginning, Lee takes two piles,
then Alex takes 2 piles again. Alex can get 2 + 4 + 4 = 10 piles in total.
If Alex takes two piles at the beginning, then Lee can take all three piles left.
In this case, Alex get 2 + 7 = 9 piles in total. So we return 10 since it's larger.
Constraints:
1 <= piles.length <= 100
1 <= piles[i] <= 10 ^ 4
算法思路:
方法1:
- min-max in recursive and with memo in optimize(博弈论minmax)
- 对于每一次递归,Alex选完,还有剩下则Lee接着选,或者先先Lee后Alex,保证操作成对出现,这样不用判断当前谁在选择
- 初始状态是Alex先选择,并且两人都会进行最佳选择
- Use dynamic programming: the states are (p, m) for the answer of piles[p:] and that given m.
- 状态数为 O(n^2)个,转移需要 O(n) 的时间,故时间复杂度为 O(n^3)
//memo[p][M]:in position p, when limitation is M,how bigger diff(res) can we obtain between Alex and Lee
//so the diff may be negative
//O(n^3) O(n^2)
//Runtime: 16 ms 49.71%
//Memory Usage: 7.5 MB
class Solution {
public:
int stoneGameII(vector<int>& piles) {
int n = piles.size();
vector<vector<int>> memo(n, vector<int>(n + 1, INT_MIN));
function<int(int, int)> solve = [&](int p, int M){
if(p >= n) return 0;
M = min(M, n);
if(memo[p][M] != INT_MIN) return memo[p][M];
if(p + 2 * M >= n) return accumulate(piles.begin() + p, piles.end(), 0);
int res = INT_MIN;
int cur = 0;
for(int x = 1; x <= 2 * M; x++) {
cur += piles[p + x - 1];
res = max(res, cur - solve(p + x, max(M, x)));
}
memo[p][M] = res;
return res;
};
int total = accumulate(piles.begin(), piles.end(), 0);
solve(0, 1);
return (total + solve(0, 1)) / 2;
}
};
方法2:改造下加法
//memo[p][M]:in position p, when limitation is M,how bigger diff(res) can we obtain between Alex and Lee
//O(n^3) O(n^2)
//Runtime: 4 ms 96.58%
//Memory Usage: 7.4 MB
class Solution {
public:
int stoneGameII(vector<int>& piles) {
int n = piles.size();
vector<int> sum(n, 0); //sum[i]: the sum of piles[i:]
sum[n - 1] = piles[n - 1];
for(int i = n - 2; i >= 0; i--) {
sum[i] = sum[i + 1] + piles[i];
}
vector<vector<int>> memo(n, vector<int>(n + 1, INT_MIN));
function<int(int, int)> solve = [&](int p, int M){
if(p >= n) return 0;
M = min(M, n);
if(memo[p][M] != INT_MIN) return memo[p][M];
if(p + 2 * M >= n) return sum[p];//accumulate(piles.begin() + p, piles.end(), 0);
int res = INT_MIN;
int cur = 0;
for(int x = 1; x <= 2 * M; x++) {
cur += piles[p + x - 1];
res = max(res, cur - solve(p + x, max(M, x)));
}
memo[p][M] = res;
return res;
};
int total = sum[0];//accumulate(piles.begin(), piles.end(), 0);
solve(0, 1);
return (total + solve(0, 1)) / 2;
}
};
方法3:改造成动态规划
//dp(i,m) 表示当前位于第 i 个位置且 M 为 m 时,先手能取到的最大的石子数量
//Runtime: 32 ms 36.20%
//Memory Usage: 7.4 MB
class Solution {
public:
int stoneGameII(vector<int>& piles) {
int n = piles.size();
vector<int> sum(n, 0); //sum[i]: the sum of piles[i:]
sum[n - 1] = piles[n - 1];
for(int i = n - 2; i >= 0; i--) {
sum[i] = sum[i + 1] + piles[i];
}
vector<vector<int>> dp(n + 1, vector<int>(n + 1, 0));
for(int i = n - 1; i >= 0; i--) {
for(int m = 1; m <= n; m++) {
for(int x = 1; x <= 2 * m && i + x <= n; x++){
dp[i][m] = max(dp[i][m], sum[i] - dp[i + x][max(m, x)]);
}
}
}
return dp[0][1];
}
};
方法4: dp改进
//dp(i, m) 表示当前位于第 i 个位置且 M 为 m 时,先手能取到的最大的石子数量
//Runtime: 12 ms 56.88%
//Memory Usage: 7.3 MB
class Solution {
public:
int stoneGameII(vector<int>& piles) {
int n = piles.size();
vector<int> sum(n, 0); //sum[i]: the sum of piles[i:]
sum[n - 1] = piles[n - 1];
for(int i = n - 2; i >= 0; i--) {
sum[i] = sum[i + 1] + piles[i];
}
vector<vector<int>> dp(n + 1, vector<int>(n + 1, 0));
for(int i = n - 1; i >= 0; i--) {
for(int m = 1; m <= i || m == 1; m++) {
for(int x = 1; x <= 2 * m; x++){
if(i + x > n) {
break;
} else {
dp[i][m] = max(dp[i][m], sum[i] - dp[i + x][max(m, x)]);
}
}
}
}
return dp[0][1];
}
};
参考资料:
https://www.bilibili.com/video/av61387911/
https://blog.csdn.net/qq_17550379/article/details/97757641
https://leetcode.com/problems/stone-game-ii/discuss/345230/Python-DP-Solution