代码随想录训练营第四十三天| ● 1049. 最后一块石头的重量 II ● 494. 目标和 ● 474.一和零

文章介绍了如何使用动态规划方法解决LeetCode中的三个背包问题:lastStoneWeightII(找到剩余最后一块石头的重量)、494.目标和(找满足目标和的组合数)和474.一和零(找用最少物品填满背包的方案)。作者详细解释了算法过程和代码实现。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

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

int lastStoneWeightII(vector<int>& stones) {
        int sum = 0;
        for (int i = 0; i < stones.size(); i++) 
        {
            sum += stones[i];
        }
        int target = sum / 2;
        vector<int> dp(target+1, 0);
        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];
    }

 这道题和昨天那道题类似,由于两个相碰的石头会剩下相差的重量,因此要将整个石堆按重量尽量平分,两堆相差的重量就是碰撞后剩下的最后一块石头的重量。因此本题物品的重量为stones[i],物品的价值也为stones[i],要放进容量为sum/2的背包中,最后返回两堆差值即可!

494. 目标和

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];
    }

本题要如何使表达式结果为target,

既然为target,那么就一定有 left组合 - right组合 = target。也就是将加号的放在一边,减号的也放在一边。

left + right = sum,而sum是固定的。right = sum - left

公式来了, left - (sum - left) = target 推导出 left = (target + sum)/2 。

target是固定的,sum是固定的,left就可以求出来。

此时问题就是在集合nums中找出和为left的组合。

而如何得到递归公式呢?只要搞到nums[i],凑成dp[j]就有dp[j - nums[i]] 种方法。也就是当前背包容量的组合数等于减去当前元素值大小的背包容量的组合数,因为减去之后的背包容量再加上这个元素就是目标容量了,所以组合数相同,因此,递推公式为:

dp[j] += dp[j - nums[i]]

 初始化要将dp[0]初始化为1,因为一个都不取就可以得到和为0的left。

474.一和零 

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];
    }

 这道题也是01背包,只不过物品的重量相当于是有两个维度,一个0一个1.

dp[i][j]:最多有i个0和j个1的strs的最大子集的大小为dp[i][j]。之后分析每个遍历的元素字符串,计算每个字符串包含的0和1的数量,然后dp[i][j] = max(dp[i][j], dp[i - zeroNum][j - oneNum] + 1);也就是说dp[i][j]的大小等于dp[0的数量减去zeroNum][1的数量减去oneNum] + 1,就是加入当前字符串或者不加入当前字符串dp[i][j],取两个中更大的那一个。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值