动态规划之0-1背包问题

前言

在这里插入图片描述

0,1背包问题满足下列条件:
在这里插入图片描述

问题分析:

一、减少规模

  1. 定义m(i, j)是背包容量为j,可选择物品为i,i+1,…,n时0-1背包问题的最优 值
  2. m(i+1, j)为可选择物品为i+1,…,n时0-1背包问题的最优值
  3. m(n, j)为可选择物品为n时0-1背包问题的最优值,此时,规模已为1
    在这里插入图片描述

二、推导递归式

判断是否放入第i件?

  1. 不放,背包当前产生价值仍为m(i+1,j)
  2. 放入,调整背包容量j-wi,背包当前产生价值为m(i+1, j-wi)+vi
    在这里插入图片描述

实例分析

在这里插入图片描述
01背包问题dp算法从第n行开始填表,n=5,即第五行,用上面递归式的第二个
在这里插入图片描述

  • 背包容量( j )小于第五个物品的重量(Wn=W5)时,不放入
  • 背包容量( j )大于等于第五个物品重量(Wn=W5)时,放入
  • 需要注意第五个物品的重量可能比背包总容量c都要大,在实现代码时需要考虑进去。

第四行

  • 背包容量为0-4时,即m(4,0),m(4,1),m(4,2),m(4,3) ,m(4,4),第四个物品的重量为5,第四个物品根本放不进去,所以按照递归式 ,m(4,0~4)=m(5,0-4)。
    在这里插入图片描述

在这里插入图片描述

  • 当背包容量大于等于物品四的容量时,我们就要考虑是否要放入物品四。是否放入,取决于放入与不放入两种情况的价值大小。如果放入物品四后总价值小于不放入物品四的总价值则不放,反之则放。
    在这里插入图片描述

  • 例如背包容量为5时,不放入物品四:总价值(Value1)=背包容量为5时放入物品五时的价值。放入物品四:总价值(Value2)=物品四的价值+背包容量为0(5-5)时放入物品五时的价值。m(4,5) = max(Value1,Value2)。而放入物品五时的价值在不同背包容量时已经在前面计算出来了。背包容量为5时总价值计算如下:
    在这里插入图片描述

  • 背包容量为9时,不放入物品四:总价值(Value1)=背包容量为9时放入物品五时的价值。放入物品四:总价值(Value2)=物品四的价值+背包容量为4(9-5)时放入物品五时的价值。m(4,9) = max(Value1,Value2)。背包容量为5时总价值计算如下:
    在这里插入图片描述
    第三行

  • 因为物品三的重量为6,所以当背包容量为0-5时,无法放入物品三。m(3,0~5)=m(4,0-5)
    在这里插入图片描述

  • 背包容量为6时,不放入物品三:总价值(Value1)=背包容量为6时放入物品四和五时的价值。放入物品三:总价值(Value2)=物品三的价值+背包容量为0(6-6)时放入物品四和五时的价值。m(3,6) = max(Value1,Value2)。
    在这里插入图片描述

  • 背包容量为10时,不放入物品三:总价值(Value1)=背包容量为10时放入物品四和五时的价值。放入物品三:总价值(Value2)=物品三的价值+背包容量为4(10-6)时放入物品四和五时的价值。m(3,10) = max(Value1,Value2)。
    在这里插入图片描述

其他行一样

实现

一、JAVA代码实现

import java.util.Scanner;

public class zero_oneBackPack{

	public static void main(String[] args) {
		Scanner sc = new Scanner(System.in);
		System.out.println("输入物品的数量:");
		int n = sc.nextInt();//物品数量
		System.out.println("输入背包容量:");
		int c = sc.nextInt();//背包容量
		int[] v = new int[n+1];//各物品的价值
		int[] w = new int[n+1];//各物品的重量
		System.out.println("输入各物品的价值:");
		for (int i = 1; i <= n; i++) {
			v[i] = sc.nextInt();			
		}
		System.out.println("输入各物品的重量:");
		for (int i = 1; i <= n; i++) {
			w[i] = sc.nextInt();			
		}
		
		int[][] cc = new int[n+1][c+1];
		for (int i =1; i <=c; i++) {//填写第n行
			if(i>=w[n]) {
				cc[n][i] = v[n];
			}else 
				cc[n][i] = 0;
			
		}
		for (int i = n-1; i>=1; i--) {//填写1~(n-1)行
			for (int j = 1; j <=c; j++) {
				if(j>=w[i]) {//背包容量大于等于第i行物品的重量时比较两种情况的总价值
					cc[i][j] = Math.max(cc[i+1][j], cc[i+1][j-w[i]]+v[i]);
				}else //背包容量小于第i行物品的重量
					cc[i][j] = cc[i+1][j];
				
			}
			
		}
		System.out.println("总价值计算情况:");
		for (int i = 1; i <=n; i++) {
			System.out.print("第"+(i)+"行:\t");
			for (int j = 1; j <=c; j++) {
				System.out.print(cc[i][j]+"\t");
				
			}
			System.out.println();
			
		}
		
	}

}

二、结果

在这里插入图片描述

三、构造物品的选取

在这里插入图片描述
我们在填表的时候,如果第i个物品不选取,则选取第i+1到n个物品,即m(i+1,1~n)。看上面的第三行和第四行,蓝色区域,m(3,6)=m(4,6),表示第三个物品没有选取。

构造:

void traceback(int c,int[][] cc){
		int[] a = new int[n];
		int j=c;
		for (int i = 0; i <n-1; i++) {//判断1-(n-1)个物品是否选取
			if(cc[i+1][j]==cc[i+2][j]) {
				a[i]=0;//相等则没有选取,对于位填0
			}else {
				a[i]=1;//不相等则没有选取,对于位填1
				j=j-w[i+1];//并且,减去相应的重量
			}
			
		}
		if(cc[n][j]==0) {//判第n个物品是否选取
			a[n-1]=0;
		}else 
			a[n-1] = 1;
		System.out.println("选取的哪些物品:");
		System.out.print("选取了:第"+"\t");
		for (int i = 0; i < a.length; i++) {
			if(a[i]==1)
				System.out.print(i+1+"\t");
		}

}

结果:
在这里插入图片描述

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值