代码随想录算法训练营 动态规划part05

一最后一块石头的重量 II

1049. 最后一块石头的重量 II - 力扣(LeetCode)

假设想要得到最优解,我们需要按照如下顺序操作石子:[(sa,sb),(sc,sd),...,(si,sj),(sp,sq)]。

其中 abcdijpq 代表了石子编号,字母顺序不代表编号的大小关系。

如果不考虑「有放回」的操作的话,我们可以划分为两个石子堆(正号堆/负号堆):

将每次操作中「重量较大」的石子放到「正号堆」,代表在这次操作中该石子重量在「最终运算结果」中应用 + 运算符

将每次操作中「重量较少/相等」的石子放到「负号堆」,代表在这次操作中该石子重量在「最终运算结果」中应用 −-− 运算符

这意味我们最终得到的结果,可以为原来 stones 数组中的数字添加 +/− 符号,所形成的「计算表达式」所表示。

其实所谓的「有放回」操作,只是触发调整「某个原有石子」所在「哪个堆」中,并不会真正意义上的产生「新的石子重量」。

什么意思呢?

假设有起始石子 a 和 b,且两者重量关系为 a≥b,那么首先会将 a 放入「正号堆」,将 b 放入「负号堆」。重放回操作可以看作产生一个新的重量为 a−b 的"虚拟石子",将来这个"虚拟石子"也会参与某次合并操作,也会被添加 +/−符号:

当对"虚拟石子"添加 + 符号,即可 +(a−b),展开后为 a−b,即起始石子 a 和 b 所在「石子堆」不变

当对"虚拟石子"添加 − 符号,即可 −(a−b),展开后为 b−a,即起始石子 a 和 b 所在「石子堆」交换

因此所谓不断「合并」&「重放」,本质只是在构造一个折叠的计算表达式,最终都能展开扁平化为非折叠的计算表达式。

综上,即使是包含「有放回」操作,最终的结果仍然可以使用「为原来 stones 数组中的数字添加 +/− 符号,形成的"计算表达式"」所表示。

1049. 最后一块石头的重量 II - 力扣(LeetCode)

复制代码
class Solution {
    public int lastStoneWeightII(int[] ss) {
        int n = ss.length;
        int sum = 0;
        for (int i : ss) sum += i;
        int t = sum / 2;
        int[][] f = new int[n + 1][t + 1];
        for (int i = 1; i <= n; i++) {
            int x = ss[i - 1];
            for (int j = 0; j <= t; j++) {
                f[i][j] = f[i - 1][j];
                if (j >= x) f[i][j] = Math.max(f[i][j], f[i - 1][j - x] + x);
            }
        }
        return Math.abs(sum - f[n][t] - f[n][t]);
    }
}

二、目标和

494. 目标和 - 力扣(LeetCode)

复制代码
    public static int findTargetSumWays(int[] nums, int s) {
        int sum = 0;
        for (int i = 0; i < nums.length; i++) {
            sum += nums[i];
        }
        // 绝对值范围超过了sum的绝对值范围则无法得到
        if (Math.abs(s) > Math.abs(sum)) return 0;

        int len = nums.length;
        // - 0 +
        int t = sum * 2 + 1;
        int[][] dp = new int[len][t];
        // 初始化
        if (nums[0] == 0) {
            dp[0][sum] = 2;
        } else {
            dp[0][sum + nums[0]] = 1;
            dp[0][sum - nums[0]] = 1;
        }

        for (int i = 1; i < len; i++) {
            for (int j = 0; j < t; j++) {
                // 边界
                int l = (j - nums[i]) >= 0 ? j - nums[i] : 0;
                int r = (j + nums[i]) < t ? j + nums[i] : 0;
                dp[i][j] = dp[i - 1][l] + dp[i - 1][r];
            }
        }
        return dp[len - 1][sum + s];
    }

三、一和零

474. 一和零 - 力扣(LeetCode)

复制代码
class Solution {
    public int findMaxForm(String[] strs, int m, int n) {
        int len = strs.length;
        // 预处理每一个字符包含 0 和 1 的数量
        int[][] cnt = new int[len][2];
        for (int i = 0; i < len; i++) {
            String str = strs[i];
            int zero = 0, one = 0;
            for (char c : str.toCharArray()) {
                if (c == '0') {
                    zero++;
                } else {
                    one++;
                }
            }
            cnt[i] = new int[]{zero, one}; 
        }

        // 处理只考虑第一件物品的情况
        int[][][] f = new int[len][m + 1][n + 1];
        for (int i = 0; i <= m; i++) {
            for (int j = 0; j <= n; j++) {
                f[0][i][j] = (i >= cnt[0][0] && j >= cnt[0][1]) ? 1 : 0;
            }
        }

        // 处理考虑其余物品的情况
        for (int k = 1; k < len; k++) {
            int zero = cnt[k][0], one = cnt[k][1];
            for (int i = 0; i <= m; i++) {
                for (int j = 0; j <= n; j++) {
                    // 不选择第 k 件物品
                    int a = f[k-1][i][j];
                    // 选择第 k 件物品(前提是有足够的 m 和 n 额度可使用)
                    int b = (i >= zero && j >= one) ? f[k-1][i-zero][j-one] + 1 : 0;
                    f[k][i][j] = Math.max(a, b);
                }
            }
        }
        return f[len-1][m][n];
    }
}

474. 一和零 - 力扣(LeetCode)

相关推荐
Dlrb12111 小时前
C语言-指针数组与数组指针
c语言·数据结构·算法·指针·数组指针·指针数组·二级指针
WL_Aurora1 小时前
Python 算法基础篇之集合
python·算法
平行侠2 小时前
A15 工业路由器IP前缀高速检索与内存压缩系统
网络·tcp/ip·算法
阿旭超级学得完3 小时前
C++11包装器(function和bind)
java·开发语言·c++·算法·哈希算法·散列表
li星野3 小时前
位运算 & 数学 & 高频进阶九题通关(Python + C++)
c++·python·学习·算法
jerryinwuhan3 小时前
hello算法,简单讲(1)
算法·排序算法
y = xⁿ3 小时前
20天速通LeetCodeday15:BFS广度优先搜索
算法·宽度优先
400分3 小时前
吃透RAG核心-----语义检索与关键字检索底层原理
算法·架构
目黑live +wacyltd3 小时前
算法备案:常见驳回原因与应对策略
人工智能·算法
磊 子4 小时前
多态类原理+四种类型转换+异常处理
开发语言·c++·算法