[M背包] lc1049. 最后一块石头的重量 II(01背包+知识理解+好题+思维)

Posted Ypuyu

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了[M背包] lc1049. 最后一块石头的重量 II(01背包+知识理解+好题+思维)相关的知识,希望对你有一定的参考价值。

1. 题目来源

链接:1049. 最后一块石头的重量 II

2. 题目解析

好难的一道 01 背包变种题…一开始还以为是选相邻石头,误判为 区间 dp

题目描述中有点误导人的意思,不需要限定 x<=y 才做后两种操作,直接可以任选两个石头 xy合并后新石头重量为 abs(x-y) 即可,当新石头质量为 0 时,则不再进入石头堆了。

那么抽象的来想一下,每次合并过程其实就是针对两个石头选择正负号的过程,一直到最后的合并,也是两个石头选择正负号的过程。那么此时,我们按照符号统一将正数、负数分类看作两堆,问题就转化成了,只要两堆石子的数值越接近,那么合并后石头越小。

看下例:

输入:stones = [2,7,4,1,8,1]
输出:1
解释:
组合 2 和 4,得到 2,所以数组转化为 [2,7,1,8,1],我将其改为 [(4-2), 7, 1, 8, 1],
组合 7 和 8,得到 1,所以数组转化为 [2,1,1,1],我将其改为 [(4-2), (8-7), 1, 1],
组合 2 和 1,得到 1,所以数组转化为 [1,1,1],我将其改为 [(4-2)-1, (8-7), 1],
组合 1 和 1,得到 0,所以数组转化为 [1],这就是最优值, [(4-2)-1, (8-7)-1],
组合 1 和 0,得到 1,所以数组转化为 [1],这就是最优值 [((4-2)-1-((8-7)-1)]

添加了一步,方便理解。我们对这最后一步做化简 4-2-1-8+7+1=1 ,再以正负号分类:4、7、1 为一类、-2 -1 -8 为一类。

经上分析,问题等价于将一堆数划分成两组,并保证两组的差值最小。

那么就变成了每个数选且只选一次,用一个容量为 sum/2 的背包,尽可能的装,看最多能装多少石头。

这里是要分成两组数,求两组数的差值尽量小,那么两组数肯定是要往 sum/2 里靠,小的一方越接近越好,将其假设为 a,大的一方就是 sum-a,则差值即为 sum-a-a,即为最优解。

综上所述,本题就是个 01 背包,容量上限为 sum/2,求尽可能填满背包的重量是多少。


总结来看,选不选问题且直选一次,转化为 01 背包后,可以自定义背包容量,就能够快速选择出不超过背包容量的合法装填方案。

关于 01 背包初始化,以及 01 背包循环内部怎么写…

  • 第二层循环背包容量是从 0 开始的,不要从 1 开始
  • 且不选择该物品一定可以不被选择,那么针对 f[i][j] = f[i-1][j] 一定是需要被执行的,所以不要写 if(j>=stones[i-1]) .... else f[i][j]=f[i-1][j],这样会导致 f[i][j]=f[i-1][j] 有些部分不会被执行,导致状态转移错误!
  • vector<vector<bool>> 中,写的是 if---else 结构,但是在此的 if 判断里面把 else 里面的语句给包含了,所以这个 else 里面的语句不论怎样都会执行…状态转移永远正确。

时间复杂度: O ( n 2 ) O(n^2) O(n2)

空间复杂度: O ( n 2 ) O(n^2) O(n2)


注意:

在这一开始将 j 手误写成了从 1 开始,出错了。等于说除过 f[0][0]=true,其余的 f[i][0]=false 因为没办法进行状态转移,所以需要在初始化的时候,将 f[i][0]=true 进行全部初始化,这样 j 从 1 开始也是可以的。

实际上在第一个 for 循环后面紧接着一个 f[i][0]=true 也是可以的,因为毕竟一个不选也是合法的。

class Solution {
public:
    int lastStoneWeightII(vector<int>& stones) {
        int n = stones.size(), sum = 0;
        for (int x : stones) sum += x;
        
        int m = sum / 2;
        vector<vector<bool>> f(n + 1, vector<bool>(m + 1)); 
        // f[i][j] 表示从前i个数中选,能否在背包下装质量恰好为j。
        
        f[0][0] = true;

        for (int i = 1; i <= n; i ++ ) 
            for (int j = 0; j <= m; j ++ ) {	// 不要把 j 写成从 1 开始...
                if (j >= stones[i - 1]) f[i][j] = f[i - 1][j] || f[i - 1][j - stones[i - 1]];
                else f[i][j] = f[i][j] || f[i - 1][j];
				
				// 另一种写法
				// f[i][j] = f[i][j] || f[i - 1][j];
                // if (j >= stones[i - 1]) f[i][j] = f[i - 1][j] || f[i - 1][j - stones[i - 1]];
            }
        
        for (int j = m; ~j; j -- ) 
            if (f[n][j])
                return sum - j - j;
        
        return sum;
    }
};

经典的 01 背包一维优化:

class Solution {
public:
    int lastStoneWeightII(vector<int>& stones) {
        int n = stones.size(), sum = 0;
        for (int x : stones) sum += x;
        
        int m = sum / 2;
        vector<bool> f(m + 1);
        
        f[0] = true;

        for (int i = 1; i <= n; i ++ ) 
            for (int j = m; j >= stones[i - 1]; j -- ) 
                f[j] = f[j] || f[j - stones[i - 1]];
            
        
        for (int j = m; ~j; j -- ) 
            if (f[j])
                return sum - j - j;
        
        return sum;
    }
};

直接背包存质量:

class Solution {
public:
    int lastStoneWeightII(vector<int>& stones) {
        int n = stones.size(), sum = 0;
        for (int x : stones) sum += x;

        int m = sum / 2;
        vector<vector<int>> f(n + 1, vector<int>(m + 1));

        f[0][0] = 0;
        for (int i = 1; i <= n; i ++ )
            for (int j = 0; j <= m; j ++ ) {
                f[i][j] = f[i - 1][j];
                if (j >= stones[i - 1]) f[i][j] = max(f[i][j], f[i - 1][j - stones[i - 1]] + stones[i - 1]);
            }
        
        int res = 1e9;
        for (int i = m; ~i; i -- ) res = min(res, sum - f[n][i] - f[n][i]);

        return res;
    }
};

以上是关于[M背包] lc1049. 最后一块石头的重量 II(01背包+知识理解+好题+思维)的主要内容,如果未能解决你的问题,请参考以下文章

1049 最后一块石头的重量 II

动态规划第六篇:01背包问题(分割等和子集 + 最后一块石头的重量 II)

动态规划第六篇:01背包问题(分割等和子集 + 最后一块石头的重量 II)

1049. 最后一块石头的重量 II

LeetCode 1049 最后一块石头的重量[动态规划] HERODING的LeetCode之路

LeetCode1049. 最后一块石头的重量 II / 牛客:毕业旅行问题(状压DP)