LeeCode 打卡day42,43 --01背包问题及其应用


知识总结

今天学习背包问题以及其应用, 重点掌握其思想以及如何将其他问题同背包问题类比起来


Leetcode 背包问题

题目说明

经典的背包, 给定指定的物品的容量和价值, 求在指定的背包容量下可以装物品的最大价值.

代码说明

一些需要记住的套路:

  1. 二维数组代表当前物品i, 背包容量j下可以装的最大价值.
  2. 一维数组是指一个滚动数组, 遍历顺序固定了, 先遍历物品, 再遍历容量, 并且需要倒序遍历.
  3. 递推公式:

dp[i][j] = Math.max(dp[i - 1][j], dp[i - 1][j - weight[i]] + value[i])

package com.backpack;
import org.junit.jupiter.api.Test;
import java.util.Arrays;

public class backpack {
    
    
    int[] weight = {
    
    1, 3, 4, 2};
    int[] value = {
    
    15, 27, 30, 20};
    int bagSize = 5;



    @Test
    void test2Dbackpack() {
    
    
        int num = weight.length;
        int[][] dp = new int[num][bagSize + 1];
        // initialize the first row
        for (int j = 0; j <= bagSize; j++) {
    
    
            // when the first item weight is larger or equal than
            // the current bag size, we can put the item into the bag
            if (weight[0] <= j) dp[0][j] = value[0];
        }

        for (int i = 1; i < num; i++) {
    
     //遍历物品
            for (int j = 1; j <= bagSize; j++) {
    
     // 遍历背包
                if (j - weight[i] < 0) {
    
    
                    dp[i][j] = dp[i - 1][j]; // 当前容量不能放得下物品i
                } else {
    
    
                    dp[i][j] = Math.max(dp[i - 1][j], dp[i - 1][j - weight[i]] + value[i]);
                }
            }
        }
        for (int i = 0; i < num; i++) {
    
    
            System.out.println(Arrays.toString(dp[i]));
        }

        System.out.println("Max value is " + dp[num - 1][bagSize]);
    }

    @Test
    void test1Dbackpack() {
    
    
        int[] dp = new int[bagSize + 1];
        dp[0] = 0;
        for (int i = 0; i < weight.length; i++) {
    
    
            for (int j = bagSize; j >= 0; j--) {
    
    
                if (j >= weight[i]) {
    
    
                    dp[j] = Math.max(dp[j], dp[j - weight[i]] + value[i]);
                }
            }
            System.out.println(Arrays.toString(dp));
        }
        System.out.println("Max value is " + dp[bagSize]);
    }
}


Leetcode 416. 分割等和子集

题目链接

题目说明

给你一个 只包含正整数 的 非空 数组 nums 。请你判断是否可以将这个数组分割成两个子集,使得两个子集的元素和相等。

在这里插入图片描述

代码说明

将问题转化成为: 能否在容量为sum/2 的背包中装满价值为sum/ 2的物品, 如果可以则能等分, 否则不能.
此处物品的体积和价值相等

class Solution {
    
    
    public boolean canPartition(int[] nums) {
    
    
        int sum = 0;
        for(int i = 0; i < nums.length; i++){
    
    
            sum += nums[i];
        }
        if(sum % 2 == 1) return false;
        int target = sum / 2;
        int[] dp = new int[target+1];
        dp[0] = 0;
        for(int i = 0; i < nums.length; i++){
    
    
            for(int j = target; j >= nums[i]; j--){
    
    
                dp[j] = Math.max(dp[j], dp[j - nums[i]] + nums[i]);   
            }
            // System.out.println(Arrays.toString(dp));
        }
        return dp[target] == target;
    }
}

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

题目链接

题目说明

有一堆石头,用整数数组 stones 表示。其中 stones[i] 表示第 i 块石头的重量。

每一回合,从中选出任意两块石头,然后将它们一起粉碎。假设石头的重量分别为 x 和 y,且 x <= y。那么粉碎的可能结果如下:

如果 x == y,那么两块石头都会被完全粉碎;
如果 x != y,那么重量为 x 的石头将会完全粉碎,而重量为 y 的石头新重量为 y-x。
最后,最多只会剩下一块 石头。返回此石头 最小的可能重量 。如果没有石头剩下,就返回 0。

在这里插入图片描述

代码说明

这道题和前面会比较类似, 如果说可以等分的, 那么就返回0, 但是如果不能等分的话, 怎么办呢?

将石头分为两组, 两组的的差值要尽可能的小. 假设总的为23, 那么target = 11, 另外一边就为12. 容量为11的背包里可以装的最大价值为: dp[target] <= 11, 那么两个背包的价值差 = (sum - dp[target]) - dp[targert]

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]);
            }
            //  System.out.println(Arrays.toString(dp));
        }
        return sum  - 2 * dp[target];
    }
}

Leetcode 494. 目标和

题目链接

题目说明

给你一个整数数组 nums 和一个整数 target 。

向数组中的每个整数前添加 ‘+’ 或 ‘-’ ,然后串联起所有整数,可以构造一个 表达式 :

例如,nums = [2, 1] ,可以在 2 之前添加 ‘+’ ,在 1 之前添加 ‘-’ ,然后串联起来得到表达式 “+2-1” 。
返回可以通过上述方法构造的、运算结果等于 target 的不同 表达式 的数目。

在这里插入图片描述

代码说明

这里有一个很巧妙的转化, target 可以当做 positive - negative组,
positive + negative = sum, 所有positive = (sum + target) / 2
问题就转化成装满容量为positive的背包有多少种方式.

需要注意的是target如果为负数时需要转成正数进行运算.

class Solution {
    
    
    public int findTargetSumWays(int[] nums, int target) {
    
    

        int sum = 0;
        for(int num : nums){
    
    
            sum += num;
        }
        if(target < 0) target = -target;

        int total = sum += target;
        if(total % 2 != 0) return 0;
        int bagSize = Math.abs(total / 2);

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

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

Leetcode 474. 一和零

题目链接

题目说明

给你一个二进制字符串数组 strs 和两个整数 m 和 n 。

请你找出并返回 strs 的最大子集的长度,该子集中 最多 有 m 个 0 和 n 个 1 。

如果 x 的所有元素也是 y 的元素,集合 x 是集合 y 的 子集 。

代码说明

class Solution {
    
    
    public int findMaxForm(String[] strs, int m, int n) {
    
    
        int[][] dp = new int[m+1][n+1];
        for(String str : strs){
    
    
            int x = 0, y = 0;
            for(char ch : str.toCharArray()){
    
    
                if(ch == '0'){
    
    
                    x++;
                }else{
    
    
                    y++;
                }
            }

            for(int i = m; i >= x; i--){
    
    
                for(int j = n; j >= y; j--){
    
    
                    dp[i][j] = Math.max(dp[i][j], dp[i-x][j-y] + 1);
                }
            }
        }

        return dp[m][n];
    }
}

猜你喜欢

转载自blog.csdn.net/weixin_45872648/article/details/131342428