背包问题总结

最近看了很多背包问题相关的算法,现对其总结如下。

几种常见的类型:

  • 0/1背包问题(特点:对第i件物品的两种选择,放或不放)

题目:

有N件物品和一个容量为V 的背包。放入第i件物品耗费的费用是Ci,得到的价值是Wi。求解将哪些物品装入背包可使价值总和最大。
基本思路:

 

空间优化:

 

  • 完全背包问题

题目:

 

基本思路:

 

解决方法:转换为0/1背包问题

最简单的想法是,考虑到第i种物品最多选⌊V /Ci⌋件,于是可以把第i种物品转化为⌊V /Ci⌋件费用及价值均不变的物品,然后求解这个01背包问题。


这两种背包问题是面试和笔试经常遇到的问题。

题目汇总:

1.lintcode 92 -背包问题

题目描述:

在n个物品中挑选若干物品装入背包,最多能装多满?假设背包的大小为m,每个物品的大小为A[i]

样例

样例 1:
	输入:  [3,4,8,5], backpack size=10
	输出:  9

样例 2:
	输入:  [2,3,5,7], backpack size=12
	输出:  12
	

挑战

O(n x m) 的时间复杂度 and O(m) 空间复杂度
如果不知道如何优化空间O(n x m) 的空间复杂度也可以通过.

注意事项

你不可以将物品进行切割。

实现1:二维DP

public class Solution {
    /**
     * @param m: An integer m denotes the size of a backpack
     * @param A: Given n items with size A[i]
     * @return: The maximum size
     */
    public int backPack(int m, int[] A) {
        // write your code here
        //dp[i][j]表示用前i种物品装到容量为j的背包所能装的最大重量
        int[][] dp=new int[A.length][m+1];
        for(int j=0;j<=m;j++){
            if(j>A[0]){
                dp[0][j]=A[0];
            }
        }
        for(int i=1;i<A.length;i++){
            for(int j=1;j<=m;j++){
                if(j>=A[i]){
                    dp[i][j]=Math.max(dp[i-1][j],dp[i-1][j-A[i]]+A[i]);
                }else{
                    dp[i][j]=dp[i-1][j];
                }
            }
        }
        return dp[A.length-1][m];
    }
}

实现2:优化为一维DP

解法1:i从1开始,i对应的维度是A.length+1,对应的第一行值全为0;

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

解法2:i从0开始,对应的维度是A.length,第一行是A[0]=3时对应的行(以第一个测试用例为例)

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

2.lintcode 125 背包II

题目描述:

有 n 个物品和一个大小为 m 的背包. 给定数组 A 表示每个物品的大小和数组 V 表示每个物品的价值.

问最多能装入背包的总价值是多大?

思路:

设定 dp[i][j] 表示前 i 个物品装入大小为 j 的背包里, 可以获取的最大价值总和. 决策就是第i个物品装不装入背包, 所以状态转移方程就是 dp[i][j] = max(dp[i - 1][j], f[i - 1][j - A[i]] + V[i])

可以使用滚动数组优化空间至 O(m).

实现1:二维DP

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

实现2:优化为一维

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

 

3.lintcode 440 完全背包问题

划重点:each item has an infinite  number available  每个物品都可以无限次使用,刚好对应到上面的完全背包问题。

基本思路跟上题一样,只不过由于可以重复,所以在最内层循环还要再加一层while循环用于遍历枚举重复的组合。时间复杂度是O(m * n * k)。

 状态转移方程

dp[i][j] 表示前 i 个物品装入大小为 j 的背包里, 可以获取的最大价值总和
dp[ 0 ][ j ] = 0 ;

dp[ i + 1 ][ j ] = max (dp[i][j - k * w[ i ] ] + k * v[ i ] )       (0 <= k * w[ i ] <= j )

上述关系写出的代码:

for(int i=0; i<n; i++)
    for(int j=0; j<=W; j++)
        for(int k=0; k*w[i]<=j; k++)
            dp[i+1][j]=max(dp[i+1][j],dp[i][j-k*w[i]]+k*v[i]);
printf("%d\n",dp[n][W]);

三重循环,时间复杂度太高。

实现1:二维数组

for(int i=0; i<n; i++)
    for(int j=0; j<=W; j++)
        if(j<w[i])
            dp[i+1][j]=dp[i][j];
        else
            dp[i+1][j]=max(dp[i][j],dp[i+1][j-w[i]]+v[i]);
printf("%d\n",dp[n][W]);

优化:一位数组

int dp[MAXN];
for(int i=0; i<n; i++)
    for(int j=w[i]; j<=W; j++)
        dp[j]=max(dp[j],dp[j-w[i]]+v[i]);
printf("%d\n",dp[W]);

 

 参考:

https://blog.csdn.net/luoshengkim/article/details/76514558

https://blog.csdn.net/a_b_c_d_e______/article/details/81512036

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值