打卡 | 动态规划:1049. 最后一块石头的重量 II 、494. 目标和、474.一和零

本文介绍了如何将石头重量问题转化为背包问题,利用动态规划(dp)方法求解最后剩余石头的最小重量,包括两种不同的背包策略:一种是基于石头重量和背包容量,另一种是针对字符串中0和1的组合。

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

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

在这里插入图片描述
思路:
尽量分成重量相等的两堆石头,相撞即得到剩余最小可能重量

转化成背包问题:石头的重量也是价值,总体重量的一半就是背包的容量。

递归五部曲:

  1. dp[j]表示 背包总容量(所能装的总重量)是j,放进物品后,背的最大重量为dp[j]。
  2. dp[j] = max(dp[j], dp[j - stones[i]] + stones[i]);
  3. 重量和价值不会为负数,所以初始化为0即可
class Solution {
    public int lastStoneWeightII(int[] stones) {
        
        int sum = 0;

        for (int i = 0; i < stones.length; i++) {
            sum += stones[i];
        }

        int target = sum / 2;

        int[] dp = new int[target + 1];

        for (int i = 0; i < stones.length; i++) {
            for (int j = target; j >= stones[i]; j--) {
                dp[j] = Math.max(dp[j], dp[j - stones[i]] + stones[i]);
            }
        }
        return sum - dp[target] - dp[target];
    }
}

在这里插入图片描述
思路:
转化为背包问题

  • lefts - rights = target
  • lefts + rights = sum -> rights = sum - lefts
  • lefts - (sum - lefts) = target -> 2lefts = target + sum
  • lefts = (target + sum) / 2
  • 此时问题转换为 容量为 (target + sum) / 2 的背包有几种装法
public int findTargetSumWays(int[] nums, int target) {

        int sum = 0;
        for (int num : nums) {
            sum += num;
        }
        if (Math.abs(target) > sum) return 0;
        if ((sum + target) % 2 != 0) return 0;
        int bagSize = (sum + target) / 2;

        int[] dp = new int[bagSize+1];

        for (int i = 0; i < nums.length; i++) {
            for (int j = bagSize; j >= nums[i]; j++) {
                // dp[i] = Math.max(dp[j], dp[j - nums[i]] + nums[i]);
                dp[j] += dp[j - nums[i]];
            }
            System.out.println(Arrays.toString(dp));
        }
        return dp[bagSize];
    }

在这里插入图片描述

  • dp[i][j]:最多有i个0和j个1的strs的最大子集的大小为dp[i][j]。
  • dp[i][j] 就可以是 dp[i - zeroNum][j - oneNum] + 1。
    • 然后我们在遍历的过程中,取dp[i][j]的最大值。
    • 所以递推公式:dp[i][j] = max(dp[i][j], dp[i - zeroNum][j - oneNum] + 1);
  • 因为物品价值不会是负数,初始为0,保证递推的时候dp[i][j]不会被初始值覆盖。
  • 01背包一定是外层for循环遍历物品,内层for循环遍历背包容量且从后向前遍历!
    • 那么本题也是,物品就是strs里的字符串,背包容量就是题目描述中的m和n。
  • 举例推导dp数组
    public int findMaxForm(String[] strs, int m, int n) {

        //dp[i][j]表示i个0和j个1时的最大子集
        int[][] dp = new int[m+1][n+1];
        int oneNum, zeroNum;

        for (String str : strs) {
            oneNum = 0;
            zeroNum = 0;
            for (char ch: str.toCharArray()) {
                if (ch == '0') {
                    zeroNum++;
                } else {
                    oneNum++;
                }
            }
            // 倒序遍历
            for (int i = m; i >= zeroNum; i--) {
                for (int j = n; j >= oneNum; j--) {
                    dp[i][j] = Math.max(dp[i][j], dp[i-zeroNum][j-oneNum] + 1);
                }
                System.out.println(Arrays.toString(dp[i]));
            }
        }
        return dp[m][n];
    }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值