晚安,全世界
0-1背包问题
今天重温了一下动态规划之中经典问题---------01背包问题--------下面,我就来分析求解01背包问题的整个过程

经典01背包的问题是:有n 个物品,它们有各自的重量和价值,现有给定容量的背包,如何让背包里装入的物品具有最大的价值总和?

注意,这里的物品我们只考虑它们各自的重量和价值,物品个数都是1个,也就是,在01背包问题中,对于每个物品,我们要不就放进背包,要不就不放

解法

现在你是个小偷,有n个宝贝在你面前,它们都有各自的重量w和价值v,你有一个容量为T的背包,问你如何使装在背包里的东西价值最大?

首先,我们根据给出的参数,宝贝数量n,宝贝的重量w和价值v,新建两个数组w[]和v[],长度都是n+1,w[i]就代表第i(1<=i<=n)件物品的重量,v[i]就代表第i(1<=i<=n)件物品的价值,根据动态规划思想,我们新建一个长度为n+1,宽度为T+1的dp[][]数组,dp[i][j]的意思就是手中有前i件宝贝的情况下放入体积为j的背包中所能获得的最大收益
下面我们将问题一般化,我是这样理解的,对于每次准备把一个宝贝放入背包中,有三种情况值得考虑

  1. 当前准备装入的宝贝比背包体积大,那没办法了,不能偷这个宝贝了
  2. 当前背包还有一定的容积,我们准备把宝贝放入背包时,哈哈,发现可以装下,这样,我们就又装了一个宝贝进去
  3. 当前背包还有一定的容积,我们准备把宝贝放入背包时,发现装不下,没办法,我们太爱这个宝贝了,只好把背包腾出这个宝贝的空间,所以要扔掉一些宝贝,刚好腾出这个新宝贝的体积就行,然后,装入新宝贝!

假设此时我们给出一个表格,假设这就是dp表吧
0-1背包问题现在我手头有一个宝贝,我们开始计算dp[1][1],此时发现我当前只有一个重量为2,价值为6的宝贝,但是此时背包体积是1,完了,偷不了了,收益此时为0,此时我们再计算dp[1][2],哈哈,发现刚好可以放的下耶,那就放吧!此时收益变成了6,开森,接下来我们计算dp[1][3],发现,尽管背包体积在变大,但是,我们此时手头可选择的只有这一个宝贝啊,所以收益还是6,同理,后面的都是6,这一列就填好了
0-1背包问题现在我手头有两个宝贝,我们开始计算dp[2][1],惨,发现此时第二个宝贝还是要比背包体积大,所以我们的收益还是没变,dp[2][1]=dp[1][1],
接着我们开始计算dp[2][2],现在可以装了哦,但是此时宝贝体积和背包体积相同,我们尝试比较看看,把背包里宝贝扔掉一部分,刚好可以腾出装新宝贝的体积,计算扔掉一部分后的宝贝收益加上新宝贝的收益和背包没有仍宝贝之前的收益哪个大,即dp[2][1] = Math.max(dp[1][1-w[2]]+v[2], dp[1][1]),同理,我们可以计算后面的收益,最终第二行结果如下
0-1背包问题不难看出,我们已经找到了递推方程
如果当前选择放入的宝贝重量大于背包重量
则dp[i][j] = dp[i-1][j]
如果当前选择放入的宝贝重量小于背包重量
则dp[i][j] = Math.max(dp[i-1][j-w[i]]+v[i], dp[i][j]);

最后的dp表如下,因为我们每一步在全局看来都是最优的,所以dp[n][T]就是我们计算出的最大收益,为15
0-1背包问题上code

package 动态规划;

import java.util.Scanner;

/*
 * 0-1背包问题
 */
public class 背包问题 {
	public static void main(String[] args) {
		Scanner in = new Scanner(System.in);
		int total = in.nextInt();//背包体积
		int n = in.nextInt();//货品数量
		int[] weight = new int[n+1];
		int[] value = new int[n+1];
		for(int i=1;i<=n;i++) {
			weight[i] = in.nextInt();
			value[i] = in.nextInt();
		}
		System.out.println(getsolution(n,weight,value,total));
	}
	
	/*
	 * 二维数组实现
	 */
	static int Solution(int nums,int[] w,int[] v,int t) {
		int[][] dp = new int[nums+1][t+1];
		for(int k=1;k<=nums;k++) {
			for(int p=1;p<=t;p++) {
				if(p>=w[k]) {//如果当前背包总容量大于当前物品的重量
					dp[k][p] = Math.max(dp[k-1][p-w[k]]+v[k], dp[k-1][p]);
				}else {//如果当前背包总量小于当前物品的重量,那这个物品就不能放
					dp[k][p] = dp[k-1][p];
				}
			}
		}
		return dp[nums][t];
	}
}

滚动数组解法(滚动更新)

import java.util.Scanner;
public class 零一背包 {
	public static void main(String[] args) {
		Scanner in = new Scanner(System.in);
		int n = in.nextInt();//物品个数
		int m = in.nextInt();//背包重量
		int[][] res = new int[n+1][2];
		for(int i=1;i<=n;i++) {
			res[i][0] = in.nextInt();//宝贝重量w
			res[i][1] = in.nextInt();//宝贝价值v
		}
		int[][] dp = new int[2][m+1];
		for(int i=1;i<=m;i++) {
			dp[0][i] = i>=res[1][0]?res[1][1]:0;
		}
		int flag = 0;
		for(int i=2;i<=n;i++) {//从第二行开始计算
			flag = 1-flag;
			for(int j=1;j<=m;j++) {//每一列进行更新
				if(j>=res[i][0]) { 
					dp[flag][j] = Math.max(dp[1-flag][j], dp[1-flag][j-res[i][0]]+res[i][1]);//滚动数组
				}else{
					dp[flag][j] = dp[1-flag][j];
				}
			}
		}
		System.out.println(Math.max(dp[0][m], dp[1][m]));
	}
}

分类:

技术点:

相关文章: