3.31 代码随想录第三十一天打卡

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

(1)题目描述:

(2)解题思路:

cpp 复制代码
class Solution {
public:
    int lastStoneWeightII(vector<int>& stones) {
        vector<int> dp(15001, 0);
        int sum = 0;
        for (int i = 0; i < stones.size(); i++) sum += stones[i];
        int target = sum / 2;
        for (int i = 0; i < stones.size(); i++) { // 遍历物品
            for (int j = target; j >= stones[i]; j--) { // 遍历背包
                dp[j] = max(dp[j], dp[j - stones[i]] + stones[i]);
            }
        }
        return sum - dp[target] - dp[target];
    }
};

(3)总结:

1.石头相撞,把石头分为重量总和基本相等的两堆(比如看示例石头总和为23,那如果分为11和12的两队相撞后重量最小为1)
2.物品的数值即是重量又是价值
3.23/2==11向下取整,sum-z*11==1
4.dpj的定义是石头重量总和

494.目标和

(1)题目描述:

(2)解题思路:

1.一维数组方法
cpp 复制代码
class Solution {
public:
    int findTargetSumWays(vector<int>& nums, int target) {
        int sum = 0;
        for (int i = 0; i < nums.size(); i++) sum += nums[i];
        if (abs(target) > sum) return 0; // 此时没有方案
        if ((target + sum) % 2 == 1) return 0; // 此时没有方案
        int bagSize = (target + sum) / 2;
        vector<int> dp(bagSize + 1, 0);
        dp[0] = 1;
        for (int i = 0; i < nums.size(); i++) {
            for (int j = bagSize; j >= nums[i]; j--) {
                dp[j] += dp[j - nums[i]];
            }
        }
        return dp[bagSize];
    }
};

2.二维数组方法:

cpp 复制代码
class Solution {
public:
    int findTargetSumWays(vector<int>& nums, int target) {
        int sum = 0;
        for (int i = 0; i < nums.size(); i++) sum += nums[i];
        if (abs(target) > sum) return 0; // 此时没有方案
        if ((target + sum) % 2 == 1) return 0; // 此时没有方案
        int bagSize = (target + sum) / 2;
        
        vector<vector<int>> dp(nums.size(), vector<int>(bagSize + 1, 0));
        
        // 初始化最上行
        if (nums[0] <= bagSize) dp[0][nums[0]] = 1; 

        // 初始化最左列,最左列其他数值在递推公式中就完成了赋值
        dp[0][0] = 1; 

        int numZero = 0;
        for (int i = 0; i < nums.size(); i++) {
            if (nums[i] == 0) numZero++;
            dp[i][0] = (int) pow(2.0, numZero);
        }

        // 以下遍历顺序行列可以颠倒
        for (int i = 1; i < nums.size(); i++) { // 行,遍历物品
            for (int j = 0; j <= bagSize; j++) { // 列,遍历背包
                if (nums[i] > j) dp[i][j] = dp[i - 1][j]; 
                else dp[i][j] = dp[i - 1][j] + dp[i - 1][j - nums[i]];
            }
        }
        return dp[nums.size() - 1][bagSize];
    }
};

(3)总结:

1.dpj 容量为j 有dpj种方法
题可以转化成我们常见的「背包模型」的问题。设我们最终选取的结果中,前面加 + 号的数字之和为 a ,前面加 - 号的数字之和为 b ,整个数组的总和为 sum ,于是我们有:

a + b = sum
a - b = target
上面两个式子消去 b 之后,可以得到 a = (sum + target) / 2
也就是说,我们仅需在 nums 数组中选择一些数,将它们凑成和为 (sum + target) / 2 即
可。问题就变成了 416. 分割等和子集 这道题。我们可以用相同的分析模式,来处理这道题。

1. 状态表示:

dpij 表示:在前 i 个数中选,总和正好等于 j ,一共有多少种选法。

2. 状态转移方程:

老规矩,根据「最后一个位置」的元素,结合题目的要求,我们有「选择」最后一个元素或者「不选择」最后一个元素两种策略:
不选 numsi :那么我们凑成总和 j 的总方案,就要看在前 i - 1 个元素中选,凑成总和为 j 的方案数。根据状态表示,此时 dpij = dpi - 1j
选择 numsi :这种情况下是有前提条件的,此时的 numsi 应该是小于等于 j 。因为如果这个元素都比要凑成的总和大,选择它就没有意义呀。那么我们能够凑成总和为j 的方案数,就要看在前 i - 1 个元素中选,能否凑成总和为 j - numsi 。根据状态表示,此时 dpij = dpi - 1j - nums\[i]
综上所述,两种情况如果存在的话,应该要累加在一起。因此,状态转移方程为:
dpij = dpi - 1j
if(numsi - 1 <= j) dpij += dpi - 1j - nums\[i- 1]

3. 初始化:

由于需要用到「上一行」的数据,因此我们可以先把第一行初始化。第一行表示不选择任何元素,要凑成目标和 j 。只有当目标和为 0 的时候才能做到,因此第一行仅需初始化第一个元素 dp00 = 1

4. 填表顺序:

根据「状态转移方程」,我们需要「从上往下」填写每一行,每一行的顺序是「无所谓的」。

5. 返回值:

根据「状态表示」,返回 dpnaim 的值。其中 n 表示数组的大小, aim 表示要凑的目标和。

474.一和零

(1)题目描述:

(2)解题思路:

cpp 复制代码
class Solution {
public:
    int findMaxForm(vector<string>& strs, int m, int n) {
        vector<vector<int>> dp(m + 1, vector<int> (n + 1, 0)); // 默认初始化0
        for (string str : strs) { // 遍历物品
            int oneNum = 0, zeroNum = 0;
            for (char c : str) {
                if (c == '0') zeroNum++;
                else oneNum++;
            }
            for (int i = m; i >= zeroNum; i--) { // 遍历背包容量且从后向前遍历!
                for (int j = n; j >= oneNum; j--) {
                    dp[i][j] = max(dp[i][j], dp[i - zeroNum][j - oneNum] + 1);
                }
            }
        }
        return dp[m][n];
    }
};

(3)总结:

1.dpij i个0 j个1 最多能装dpij个物品
相关推荐
JieE2121 天前
LeetCode 226. 翻转二叉树|JS 递归超详细拆解,二叉树入门经典题
javascript·算法
JieE2121 天前
LeetCode 104. 二叉树的最大深度|递归思路超详细拆解
javascript·算法
vivo互联网技术1 天前
CVPR 2026 | 全新强化学习框架 BeautyGRPO:重塑真实人像
算法·大模型·cvpr·影像
Darling噜啦啦1 天前
列表转树算法深度解析:从 Map 到 Reduce 的两种实现,面试高频考点
数据结构·算法·面试
clint4561 天前
C++进阶(1)——前景提要
c++
用户497863050731 天前
(一)小红的数组操作
算法·编程语言
夜悊2 天前
C++代码示例:进制数简单生成工具
c++
怕浪猫2 天前
Electron 系列文章封面图
算法·架构·前端框架
郝学胜_神的一滴2 天前
CMake 021: IF 条件判据详诠
c++·cmake