20210608 每日一题 最后一块石头的重量 II

题目

题目链接

代码

class Solution {
public:
    int lastStoneWeightII(vector<int>& stones) {

    }
};

方法一:二维动态规划

分析

该题目与 20210607 每日一题 目标和 类似。假设所有石头和为 s u m sum sum,将石头分为大小两堆分别用 a a a b b b 表示, t a r g e t target target 为最后一块石头的重量,则有: a + b = s u m a − b = t a r g e t \begin{aligned} & a+b=sum \\ & a-b=target \end{aligned} a+b=sumab=target 因此我们可以得到:
b = s u m − t a r g e t 2 ,   b ∈ N b=\dfrac{sum-target}{2},\ b \in N b=2sumtarget, bN 当最后一块石头的重量 t a r g e t target target 取最小时, b b b 应该取最大值,因此可以将问题转化成能在数组中抽取石头组成和不超过 b b b 的最大值。设定二维数组 d p [ i ] [ j ] dp[i][j] dp[i][j] 表示在前 i i i 个元素中,组成和不超过 j j j 的最大值。
在二维数组 d p [ i ] [ j ] dp[i][j] dp[i][j] 中,起始值均为 0 0 0。对于第 i i i 块石头有选和不选两种情况:

  • 如果 j ≥ s t o n e s [ i ] j \geq stones[i] jstones[i] 时,如果不选 s t o n e s [ i ] stones[i] stones[i],最大值为 d p [ i − 1 ] [ j ] dp[i-1][j] dp[i1][j];如果选 s t o n e s [ i ] stones[i] stones[i],最大值为 d p [ i − 1 ] [ j − s t o n e s [ i ] ] + s t o n e s [ i ] dp[i-1][j-stones[i]]+stones[i] dp[i1][jstones[i]]+stones[i],因此总的最大值为: m a x ( d p [ i − 1 ] [ j ] , d p [ i − 1 ] [ j − s t o n e s [ i ] ] + s t o n e s [ i ] ) max(dp[i-1][j],dp[i-1][j-stones[i]]+stones[i]) max(dp[i1][j]dp[i1][jstones[i]]+stones[i])
  • 如果 j < s t o n e s [ i ] j < stones[i] j<stones[i] 时,则不能选 s t o n e s [ i ] stones[i] stones[i],最大值为 d p [ i − 1 ] [ j ] dp[i-1][j] dp[i1][j]

综上所述 d p [ i ] [ j ] dp[i][j] dp[i][j] 的状态转移方程可表示为:
d p [ i ] [ j ] = { m a x ( d p [ i − 1 ] [ j ] , d p [ i − 1 ] [ j − s t o n e s [ i ] ] + s t o n e s [ i ] ) i f   j ≥ s t o n e s [ i ] d p [ i − 1 ] [ j ] i f   j < s t o n e s [ i ] dp[i][j] = \begin{cases} max(dp[i-1][j],dp[i-1][j-stones[i]]+stones[i]) &if\ j \geq stones[i]\\ dp[i-1][j] &if\ j < stones[i] \end{cases} dp[i][j]={max(dp[i1][j]dp[i1][jstones[i]]+stones[i])dp[i1][j]if jstones[i]if j<stones[i] d p [ n ] [ b ] dp[n][b] dp[n][b] 即为问题答案。

代码

class Solution {
public:
    int lastStoneWeightII(vector<int>& stones) {
        int sum = accumulate(stones.begin(), stones.end(), 0);
        int n = stones.size(), b = sum / 2;
        vector<vector<int>> dp (n + 1, vector<int> (b + 1, 0));
        for(int i = 1; i <= n; ++i) {
            for(int j = 0; j <= b; ++j) {
                if(j >= stones[i - 1]) {
                    dp[i][j] = max(dp[i - 1][j], dp[i - 1][j - stones[i - 1]] + stones[i - 1]);
                } else {
                    dp[i][j] = dp[i - 1][j];
                }
            }
        }
        return sum - 2 * dp[n][b];
    }
};

复杂度分析

  • 时间复杂度: O ( n × b ) O(n \times b) O(n×b),其中 n n n 是数组长度, b b b 是选取石块的最大值和。
  • 空间复杂度: O ( n × b ) O(n \times b) O(n×b),其中 n n n 是数组长度, b b b 是选取石块的最大值和,实现动态规划需要创建 n × b n \times b n×b 的二维数组 d p dp dp

方法二:一维动态规划

分析

根据方法一的状态转移方程可知,更新 d p [ i ] [ ] dp[i][] dp[i][] 的每个元素值时,只依赖于 d p [ i − 1 ] [ ] dp[i-1][] dp[i1][] 的元素值。因此,可以使用一维滚动数组 d p [ j ] dp[j] dp[j] 来替代二维数组 d p [ i ] [ j ] dp[i][j] dp[i][j] 对方法三进行空间上的优化。 d p [ j ] dp[j] dp[j] 的状态转移方程可表示为:
d p [ j ] = d p [ j ] + d p [ j − s t o n e s [ i ] ] dp[j] = dp[j]+dp[j-stones[i]] dp[j]=dp[j]+dp[jstones[i]] d p [ b ] dp[b] dp[b] 即为问题答案。

要点

根据方法三的状态转移方程,实现时内层循环需采用倒序遍历的方式更新元素值。

代码

class Solution {
public:
    int lastStoneWeightII(vector<int>& stones) {
        int sum = accumulate(stones.begin(), stones.end(), 0);
        int b = sum / 2;
        vector<int> dp (b + 1, 0);
        for(auto stone : stones) {
            for(int j = b; j >= stone; --j) {
                dp[j] = max(dp[j], dp[j - stone] + stone);
            }
        }
        return sum - 2 * dp[b];
    }
};

复杂度分析

  • 时间复杂度: O ( n × b ) O(n \times b) O(n×b),其中 n n n 是数组长度, b b b 是负值元素和。
  • 空间复杂度: O ( b ) O(b) O(b),其中 b b b 是选取石块的最大值和,实现动态规划需要创建 b b b 的一维滚动数组 d p dp dp

拓展

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值