算法总结 - 背包问题总结

题目:backpack I (每个物品取一次,求最多能装多少物品)

Given n items with size Ai, an integer m denotes the size of a backpack. How full can you fill this backpack?

Input:

  • 物品的大小:: int[]
  • 背包的大小 ::int

Output:

  • 背包最多能装多少:: int

Intuition:

典型的DP问题,dp array存档背包大小为j时,dp[i]表示在装或者不装第 i - 1号物品时最多能装多少物品。

  • 不装第 i 号的物品: dp[i - 1][j]
  • 装第 i 号物品: dp[i - 1][j - A[i - 1]] + A[i - 1]
    举个例子: 物品大小[3, 4, 8, 5], 背包size为10
    那么在每次装一个物品时,dp array的更新如下:
    Array List: 0 0 0 3 3 3 3 3 3 3 3
    Array List: 0 0 0 3 4 4 4 7 7 7 7
    Array List: 0 0 0 3 4 4 4 7 8 8 8
    Array List: 0 0 0 3 4 5 5 7 8 9 9
    Code
    TC: (nm) SC: (nm)
public int backPack(int m, int[] A) {
        // write your code here
        if (A == null || A.length == 0){
            return 0;
        }
        
        int[][] dp = new int[A.length + 1][m + 1];
        
        for (int i = 1; i <=A.length; i++){
            for (int j = 0; j <= m; j++){
            
                if (j >= A[i - 1]) {
                    dp[i][j] = Math.max(dp[i - 1][j], 
                    dp[i - 1][j - A[i - 1]] + A[i - 1]);
                } else {
                    dp[i][j] = dp[i - 1][j];
                }
              
            }
        }
        return dp[A.length][m];
    }

当然,遇到二维数组的时候我们会考虑是不是要进行空间压缩,使用一维数组。
TC: (nm) SC: (m)
Code

 public int backPack(int m, int[] A) {
        // write your code here
        if (A == null || A.length == 0){
            return 0;
        }
        
        int[] dp = new int[m + 1];
        
        for (int i = 0; i <A.length; i++){
            for (int j = m; j >= 0; j--){
            
                if (j >= A[i]) {
                    dp[j] = Math.max(dp[j], dp[j - A[i]] + A[i]);
                } 
            }

        }
        return dp[m];
    }

题目:backpack II (每个物品取一次,求最多能装物品的最大价值)

Given n items with size Ai and value Vi, and a backpack with size m. What’s the maximum value can you put into the backpack?

Input:

  • 物品的大小:: int[]
  • 物品的价值:: int[]
  • 背包的大小 ::int

Output:

  • 背包能装物品的最大价值:: int

Intuition:

基本思想和backpack I是一样的,不同之处就在于我们更新的是物品的价值而不是物品的大小。

  • 不装第 i 号的物品: dp[i - 1][j]
  • 装第 i 号物品: dp[i - 1][j - A[i - 1]] + V[i - 1]
    例子: 物品大小[2, 3, 5, 7], 物品价值[1, 5, 2, 4],背包size为10
    那么在每次装一个物品时,dp array的更新如下:
    Array List: 0 0 1 1 1 1 1 1 1 1 1
    Array List: 0 0 1 5 5 6 6 6 6 6 6
    Array List: 0 0 1 5 5 6 6 6 7 7 8
    Array List: 0 0 1 5 5 6 6 6 7 7 9
    Code
    TC: (nm) SC: (nm)
public int backPack(int m, int[] A) {
        // write your code here
        if (A == null || A.length == 0){
            return 0;
        }
        
        int[][] dp = new int[A.length + 1][m + 1];
        
        for (int i = 1; i <=A.length; i++){
            for (int j = m; j >= 0; j--){
            
                if (j >= A[i - 1]) {
                    dp[i][j] = Math.max(dp[i - 1][j], 
                    dp[i - 1][j - A[i - 1]] + V[i - 1]);
                } else {
                    dp[i][j] = dp[i - 1][j];
                }
              
            }
        }
        return dp[A.length][m];
    }

同样的可以压缩空间
Code
TC: (nm) SC: (m)

public int backPackII(int m, int[] A, int[] V) {
        // write your code here
         // write your code here
        if (A == null || A.length == 0){
            return 0;
        }
        
        int[] dp = new int[m + 1];
        
        for (int i = 0; i <A.length; i++){
            for (int j = m; j >= 0; j--){
            
                if (j >= A[i]) {
                    dp[j] = Math.max(dp[j], dp[j - A[i]] + V[i]);
                } 
            }
        }
        return dp[m];
    }

题目:backpack III (每个物品可取无限次,求最多能装物品的最大价值)

Given n items with size Ai and value Vi, and a backpack with size m. What’s the maximum value can you put into the backpack?

Input:

  • 物品的大小:: int[]
  • 物品的价值:: int[]
  • 背包的大小 ::int

Output:

  • 背包能装物品的最大价值:: int

Intuition:

更新dp array值的策略是一样的。不过这次更新的顺序不再是从后到前,而是反过来了。这样在后半部的更新可以利用之前的值。
例子: 物品大小[2, 3, 5, 7], 物品价值[1, 5, 2, 4],背包size为10
那么dp array的更新如下:
Array List: 0 0 1 1 2 2 3 3 4 4 5
Array List: 0 0 1 5 5 6 10 10 11 15 15
Array List: 0 0 1 5 5 6 10 10 11 15 15
Array List: 0 0 1 5 5 6 10 10 11 15 15
Code
TC: O(nm) SC: O(m)

 public int backPackIII(int[] A, int[] V, int m) {
        // write your code here
       // write your code here
         // write your code here
        if (A == null || A.length == 0){
            return 0;
        }
        
        int[] dp = new int[m + 1];
        
        for (int i = 0; i <A.length; i++){
            for (int j = 0; j <= m; j++){
            
                if (j >= A[i]) {
                    dp[j] = Math.max(dp[j], dp[j - A[i]] + V[i]);
                } 
            }
        }
        return dp[m];
    }

题目:backpack IV (每个物品可取无限次,从中找出所有的和为 target 的组合个数)

Given an integer array nums with all positive numbers and no duplicates, find the number of possible combinations that add up to a positive integer target.

Input:

  • 物品的大小:: int[]
  • 目标值 ::int

Output:

  • 所有的和为目标的组合个数:: int

Intuition:

用一个dp array存0到target的可能组合数,dp[i]的组合数可以根据之前组合数求得。 dp[i] += dp[i - num[i]] 或者 dp[i + num[i]] += dp[i]

Code
TC: O(n*m) SC: O(m)

public int backPackIV(int[] nums, int target) {
        // write your code here
        if(nums == null || nums.length == 0) return 0;
        int[] dp = new int[target + 1];
        
        dp[0] = 1;
        
        
        for(int n: nums){
            for(int i = 0; i <= target; i++){
                if(n + i <= target){
                    dp[i + n] += dp[i] ;
                }
            }
        }
        return dp[target];
    }

题目:backpack V (每个物品可取一次,从中找出所有的和为 target 的组合个数)

Given an integer array nums with all positive numbers and no duplicates, find the number of possible combinations that add up to a positive integer target.

Input:

  • 物品的大小:: int[]
  • 目标值 ::int

Output:

  • 所有的和为目标的组合个数:: int

Intuition:

与上一题不同,单次选择的话更新dp array的顺序应该从后到前。
Code
TC: O(mn) SC: O(m)

public int backPackV(int[] nums, int target) {
       // write your code here
        if(nums == null || nums.length == 0) return 0;
        int[] dp = new int[target + 1];
        
        dp[0] = 1;
        for(int n: nums){
            for(int i = target; i >= 0; i--){
                if(n + i <= target){
                    dp[i + n] += dp[i] ;
                }
            }
        }
        return dp[target];
    }

题目:backpack VI (每个物品可取无限次,数的顺序不同则会被认为是不同的组合,从中找出所有的和为 target 的组合个数)

Given an integer array nums with all positive numbers and no duplicates, find the number of possible combinations that add up to a positive integer target.

Input:

  • 物品的大小:: int[]
  • 目标值 ::int

Output:

  • 所有的和为目标的组合个数:: int

Intuition:

这个题与backpackIV 不同的一点就是同样的数,用在组合不同的位置可认为是有效的解。那么可以理解成,我们在每个size装物品的时候需要每个物品都试一遍,那么对物品size的循环就应该成为内循环了。
Code
TC:O(mn) SC: O(m)

public int backPackVI(int[] nums, int target) {
        // write your code here
        if(nums == null || nums.length == 0) return 0;
        int[] dp = new int[target + 1];
        
        dp[0] = 1;
         
        for(int i = 0; i <= target; i++){
           for(int n: nums){
                if(n + i <= target){
                    dp[i + n] += dp[i];
                }
            }
        }
        return dp[target];
    }
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值