LeetCode:1140. Stone Game II

博客介绍了LeetCode中的1140题——石头游戏II,重点讨论了如何使用从上至下、带备忘录的自顶向下动态规划方法来解决这个问题。博主分享了在尝试自底向上解法失败后,转向自然递归形式并采用记忆化搜索的思路,特别提到了dp大小的选取,指出选择32作为M的最大值的原因,并提到这道题对于理解记忆化自顶向下动态规划的重要性。
摘要由CSDN通过智能技术生成

在这里插入图片描述
这一题意义重大,是我一直忽视的动态规划中:从上至下+记忆化的方法。也就是“带备忘的自顶向下法”
这一题我是开始准备按照以往思路想自底向上来做的,也就是将问题按照规模由小到大排序。但是发现不好做。相反,按照自然的递归形式写程序是最好的。这时候就应该想到带备忘的自顶向下法

class Solution {
public:
    int stoneGameII(vector<int>& piles) {
        int sz = piles.size();
        if (sz == 0)
            return 0;
        vector<vector<int>> dp(sz, vector<int>(32, 0));//大小的选取
        vector<int> sums(sz, 0);
        sums[sz-1] = piles[sz-1];
        for (int i = sz-2; i >= 0; --i)
            sums[i] = piles[i] + sums[i+1];
        return helper(piles, 0, 1, dp, sums);
    }
private:
    int helper(vector<int>& piles, int i, int M, vector<vector<int>>& dp, vector<int>& sums) {//需要注意,这个函数返回的就是dp值
        if (i >= piles.size())
            return 0;
        if (2*M >= piles.size()-i)//这里也不需要记下来了,因为要用的时候直接这里返回了,不需要到后面了
            return sums[i];
        if (dp[i][M] != 0)
            return dp[i][M];
        int minVal = INT_MAX;
        for (int x = 1; x <= 2 * M; ++x) 
            minVal = min(minVal, helper(piles, i+x, max(M, x), dp, sums));//这里直接把函数返回值当作dp值来用
        dp[i][M] = sums[i] - minVal;//记忆化方法
        return dp[i][M];
    }
};
//dp[i][m]:从i位置开始,M为m,第一个拿的人所能拿到的最多的石头的数目。
//dp[i][m] = sums[i] - min(dp[i+x][max(m, x)]), x:1->2*m
//具体解释为:需要找到一个x的大小,使对手在i+x开始,M为max(m, x)这个情况下拿到的最少(对手也是相同的策略来拿的)。使用总共的减去对手拿的,就是我拿的。

//题目一个重要前提就是:两个人都是按照“同样的”最优策略来拿的。

关于dp大小的选取:
第二个大小的含义使M的大小,选取32的含义是因为最大为32,因为加倍M最快的方式是1+2+4+8+16+32+64,而且我们用不到64(helper函数中第二个判断直接返回了),所以32就ok了。(当然也可以设置为sz)

这是很重要的一题,之后如果还碰到这种自顶向下的记忆化方法的题目再添加到这儿。

另外,更方便的,使用map的用法:

class Solution {
public:
    int stoneGameII(vector<int>& piles) {
        map<pair<int, int>, int> m;
        int sz = piles.size();
        vector<int> sums(sz, 0);
        sums[sz-1] = piles[sz-1];
        for (int i = sz-2; i >= 0; --i)
            sums[i] = piles[i] + sums[i+1];
        return helper(m, piles, 1, 0, sums);
    }
private:
    int helper(map<pair<int, int>, int>& m, vector<int>& piles, int M, int i, vector<int>& sums) {
        if (piles.size() - i <= 2 * M)
            return sums[i];
        auto p = make_pair(M, i);
        if (m.find(p) != m.end())
            return m[p];
        int val = 0;
        for (int x = 1; x <= 2*M; ++x) {
            val = max(val, sums[i] - helper(m, piles, max(M, x), i+x, sums));
        }
        m[p] = val;
        return val;
    }
};
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值