背包型动态规划

这篇博客介绍了动态规划在解决背包问题中的应用,包括01背包和完全背包的经典算法及优化。01背包允许每个物品使用一次,完全背包则允许无限次。通过动态规划的转移方程,可以求解不同限制下的背包问题,并通过滚动数组和一维数组等技巧进行空间优化。示例代码展示了如何实现这些算法。

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

背包问题算是很经典的动态规划题型了,刚开始接触很容易想当然的用贪心来做…但是很容易能够举出反例。
大致可以分为01背包、多重背包和完全背包。

01背包

题目内容:给定一个容量为w的背包和n个重量不同的物体,每个物体只能使用一次,问有多少种填满背包的方案。

思路:从最后一个物体n开始考虑,要使背包装的重量为w,有两种情况:
①背包无法装入n,则前n个物体能装满背包的最大方案数等于前n-1个物体能装满背包的最大方案数。
②背包能够装入n,此时考虑前n个物体能装满背包的最大方案数等于{前n-1个物体能装满背包的最大方案数}+{装入n后,前n-1个物体装满背包容量为w-n的背包的最大方案数}。

设f[i][j]表示当背包容量为j时,前i物体所能组合出的能够装满容量为j的背包的最大方案数, n[i]为第i个物体的重量。可以推出转移方程:
f[ i ][ j ] = f[ i-1 ][ j ], n[ i ] > j;
f[ i ][ j ] = f[ i-1 ][ j ] + f[ i-1 ][ j - n[i-1] ],n[ i ] <= j;
初始条件为f[0][0]=1。

例 领扣 563.背包问题Ⅴ

https://www.lintcode.com/problem/backpack-v
关于优化
由于前i个物体只与前i-1个物体有关,所以可以考虑使用滚动数组。
之后,继续观察发现,i对应的j只于i-1对应的0~j有关系,所以可以继续优化使用一维数组。

public class P563 {
	/**
     * @param nums: an integer array and all positive numbers
     * @param target: An integer
     * @return: An integer
     */
//    public int backPackV(int[] nums, int target) {
//    	// write your code here
//    	int n = nums.length;
//    	int[][] f = new int[n+1][target+1];
//    	f[0][0] = 1;
//    	
//    	for(int i=1; i<=n; i++){
//    		for(int j=0; j<=target; j++){  //注意这里是从0开始
//    			f[i][j] = f[i-1][j];
//                if(j>=nums[i-1]){
//                    f[i][j] += f[i-1][j-nums[i-1]];
//                }
//    		}
//    	}
//    	return f[n][target];
//    }
	public int backPackV(int[] nums, int target) {
    	// write your code here
    	int n = nums.length;
    	int[] f = new int[target+1];
    	f[0] = 1;
    	
    	for(int i=1; i<=n; i++){
    		for(int j=target; j>=nums[i-1]; j--){  //注意这里是从target开始
                f[j] += f[j-nums[i-1]];
    		}
    	}
    	return f[target];
    }
}

例 有价值的01背包 领扣125.背包问题Ⅱ

https://www.lintcode.com/problem/125/
题目:给定一个容量为w的背包和n个重量不同的物体,每个物体只能使用一次,物体的重量分别为n0,n1,n2…; 对应的价值分别为v0,v1,v2…;背包最多能装多少价值的物品。

此题若物体可以划分成小块,则采取贪心策略选择最具性价比的物品即可。但是此题物品不可分割。
思路与之前类似,设f[i][j]表示用前i物体凑成重量j时的最大价值。可以推出转移方程:
f[ i ][ j ] = max{ f[ i-1 ][ j ], f[ i-1 ][ j-n[i-1] ] + v[ i-1 ] } , j>=n[ i-1 ];

public class Solution {
    /**
     * @param m: An integer m denotes the size of a backpack
     * @param A: Given n items with size A[i]
     * @param V: Given n items with value V[i]
     * @return: The maximum value
     */
    public int backPackII(int m, int[] A, int[] V) {
        // write your code here
    	int n = A.length;
    	if( n==0 ) return 0;
    	int[] f = new int[m+1];
    	for(int i=0; i<n; i++){
    		for(int j=m; j>=A[i]; j--){  //从m开始
    			f[j] = Math.max(f[j], f[j-A[i]]+V[i]);
    		}
    	}
    	return f[m];
    }
}

完全背包

题目内容:给定一个容量为w的背包和n种重量不同的物体,每种物体可以使用多次,问有多少种填满背包的方案。

与01背包的差别就在同一种物体可以重复使用多次。

思路:设f[i]表示当背包容量为i时,前n物体所能组合出的能够装满容量为i的背包的最大方案数, n[i]为第i种物体的重量。由于每种物体可以使用多次,所以最后放入的物体可能是1,2,3,…,n;依次枚举小于等于i的物体即可得到转移方程:
f[ i ] = f[ i-n[k0] ] + f[ i-n[k1] ] + … +f[ i-n[kj] ], 其中n[k0],n[k1],…,n[kj]均小于等于i;
初始条件为f[0] = 1;即一个物体也不放入。

例 领扣 564.背包问题Ⅳ

https://www.lintcode.com/problem/combination-sum-iv/

public class Solution {
    /**
     * @param nums: an integer array and all positive numbers, no duplicates
     * @param target: An integer
     * @return: An integer
     */
    public int backPackVI(int[] nums, int target) {
        // write your code here
    	int n = nums.length;
    	int[] f = new int[target+1];
    	f[0] = 1;
    	for(int i=1; i<=target; i++){
    		for(int j=0; j<n; j++){ 
    			if(i>=nums[j]){
    				f[i] += f[i-nums[j]];
    			}
    		}
    	}
    	return f[target];
    }
}

例 有价值的完全背包 领扣 440背包问题Ⅲ

https://www.lintcode.com/problem/backpack-iii/

设f[i][j]为用前i种物体凑成重量j时具有的最大价值,则:
f[ i ] [ j ] = max{ f[ i-1 ] [ j ], f[ i-1 ] [ j-A[i-1] ] + V[ i-1 ], f[ i-1 ] [ j-2A[i-1] ] + 2V[ i-1 ],…, f[ i-1 ] [ j-kA[i-1] ] + kV[ i-1 ]}, 其中j>=kA[i-1];

观察max{ f[ i-1 ] [ j-A[i-1] ] + V[ i-1 ], f[ i-1 ] [ j-2A[i-1] ] + 2V[ i-1 ],…, f[ i-1 ] [ j-kA[i-1] ] + kV[ i-1 ]}, 当提出一个V[i-1]时,式子变为:max{ f[ i-1 ] [ j-A[i-1] ] , f[ i-1 ] [ j-2A[i-1] ] + V[ i-1 ],…, f[ i-1 ] [ j-kA[i-1] ] + (k-1)V[ i-1 ]} + V[i-1] = f[ i ][ j-A[i-1] ] + V[i-1];

所以得出最终的转移方程为:f[ i ] [ j ] = max{ f[ i-1 ] [ j ], f[ i ][ j-A[i-1] ] + V[i-1]};

优化后可设 f[i] 为当背包重量为i时能装入的最大价值。

public class Solution {
    /**
     * @param A: an integer array
     * @param V: an integer array
     * @param m: An integer
     * @return: an array
     */
    public int backPackIII(int[] A, int[] V, int m) {
    	// write your code here
    	int n = A.length;
    	if( n==0 ) return 0;
    	int[] f = new int[m+1];
    	for(int i=0; i<n; i++){
    		for(int j=A[i]; j<=m; j++){
    			f[j] = Math.max(f[j], f[j-A[i]]+V[i]);
    		}
    	}
    	return f[m];
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值