1140. Stone Game II

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

https://www.bilibili.com/video/av71976018

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值