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

0-1背包问题的描述:有吉他,音响和电脑三件物品,它们的重量分别是1磅、4磅和3磅,它们的价值分别是1500、3000和2000,现在给你个承重为4磅的背包,如何让背包里装入的物品具有最大的价值总和?

老师给我们这个问题的时候说的是一个小偷入室偷窃,问如何才能使偷到的东西具有最大的价值。这道题是动态规划很典型的问题,也是一个著名的NP难问题。话不多说直接进入正题。

  1. 思路分析

算法的主要思想是利用动态规划来解决,每次遍历到第i个物品,根据w[i]和v[i]来确定是否需要将该物品放入背包中,即对于给定的n个物品,设w[i]、v[i]分别为第i个物品的重量和价值,C为背包的容量,再令v[i][j]表示在前i个物品中能够装入容量为j的背包中的最大价值。

在这里插入图片描述

  1. 具体实现

我们通过遍历填表来寻找这个算法的规律,当i=1时,代表只有第一个物品–吉他,j表示背包的容量,从1-4,当背包的容量为1时,吉他可以放入,因为i=1,所以物品只有吉他,在背包容量为2、3、4时也只能放入吉他,如图所示:
在这里插入图片描述

当i=2时,这时新商品为音响,重量为4磅,背包容量为1、2、3磅时无法放入,所以这时采用上个单元格的解法,当j=4时,背包容量为4,能够放入音响,将它的价值和前一个单元格的最大价值进行比较,装入最大价值的物品。
在这里插入图片描述

当i=3时,新物品为电脑,同理当背包容量为1、2时之装入吉他,容量为3时,比较价值然后装入电脑,当背包容量为4时,有两种装法:一种是背包中放吉他和电脑;一种是只放入音响;到底是怎么放入背包中的价值最大呢,我们很容易的就判断出当然是前者的价值最大。但是计算机又如何判断出来的呢?
在这里引入一个规律:
当新放入物品的重量大于背包的容量时,v[i][j]=v[i-1][j] ,就直接使用上一个单元格的装入策略。
当准备加入新增物品的容量小于当前背包的容量时,v[i][j]=max{v[i-1][j],v[i]+v[i-1][j-w[i]]}。代码的意思就是把上一个单元格装入的最大价值和 当(前物品价值+剩余背包空间容量价值) 进行比较,取最大值。
在这里插入图片描述
最后一个单元格就是背包装取物品价值的最大值。

  1. 具体代码

import java.util.*;

public class KnapsackProblem{
	
	public static void main(String[] args){
		int[] weight = {1, 4, 3};   // 物品的重量
		int[] value = {1500, 3000, 2000};  // 物品的价值
		int m =4;  // 背包的容量
		int n = value.length;  // 物品的个数
		
		// 创建二维数组
		// v[i][j] 表示在前i个物品中能够装入容量为j的背包中的最大价值
		int[][] v = new int[n+1][m+1];   // 因为第一行和第一列要置零 所以容量和数目要加1
		
		//创建一个二维数组path 记录物品装入背包过程
		int[][] path = new int[n+1][m+1];
		
		// 初始化第一行和第一列 分别置零
		for(int i = 0; i < v.length; i++){
			v[i][0] = 0;  // 将第一列设置为零
		}
		for(int j = 0; j < v[0].length; j++){
			v[0][j] = 0; // 将第一行设置为零
		}
		
		for(int i = 1; i < v.length; i++){
			for(int j = 1; j< v[0].length; j++){
				if(weight[i-1] > j){  
					//  当准备新加入的物品的重量大于当前背包的容量时,直接使用上一个单元格的信息;
					v[i][j] = v[i-1][j];
				}
				else{
					//  当准备新加入的物品的重量小于当前背包的容量时, 比较(上一个单元格装入的最大值 和 当前商品价值+剩余空间物品价值) 取最大值
					if(v[i-1][j] >= value[i-1]+v[i-1][j-weight[i-1]]){
						v[i][j] = v[i-1][j];
					}
					else{
						v[i][j] = value[i-1]+v[i-1][j-weight[i-1]];
						path[i][j] = 1;  // 记录新物品放入情况
					}
				}
			}
		}
		
		// 打印输出结果
		for(int i=1; i < v.length; i++){
			for(int j = 1; j < v[0].length;j++){
				System.out.printf("%-6d",v[i][j]);
			}
			System.out.printf("\n");
		}
		System.out.printf("物品放入记录:\n");
		int i = path.length - 1;  // 行的最大下标
		int j = path[0].length - 1;  // 列的最大下标
		while( i > 0 && j > 0 ){
			if(path[i][j] == 1){
				System.out.println("第" + i + "个物品放入背包中");
				j -= weight[i-1];
			}
			i--;
		} 
		System.out.printf("背包中物品的总价值为:%d\n",v[n][m]);
	}
	
}

这是这个算法的核心思想,只要把这里想明白,这个问题就变得很简单。
在这里插入图片描述

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值