问题:
给定一组物品,每种物品都有自己的重量和价格,在限定的总重量内,我们如何选择,才能使得物品的总价格最高。问题的名称来源于如何选择最合适的物品放置于给定背包中。(每个物品只能选择一次)。
推导
零一背包问题是一个经典的动态规划问题,常见于算法和计算机科学的课程中。在零一背包问题中,给定一组物品,每个物品有自己的重量和价值,以及一个固定的背包容量,我们需要确定如何选择装入背包,使得装入背包的物品总价值最大,且总重量不超过背包的容量。
以下是解决零一背包问题的一种常见方法,称为动态规划:
-
定义状态: 定义一个二维数组
dp[i][j]
,其中dp[i][j]
表示在前i
个物品中,背包容量为j
时所能达到的最大价值。 -
状态转移方程: 对于每个物品
i
,可以选择将其装入背包或不装入背包。如果选择装入物品i
,则当前背包容量为j
时的最大价值为dp[i-1][j-w[i]] + v[i]
,其中w[i]
表示物品i
的重量,v[i]
表示物品i
的价值。如果不选择装入物品i
,则当前背包容量为j
时的最大价值为dp[i-1][j]
。因此,状态转移方程为
dp[i][j] = max(dp[i-1][j-w[i]] + v[i], dp[i-1][j])
dp初始化:背包容量为0时,价值为0。 第零个物品,价值任然为零。第一个物品时,如果背包容量大于物品重量,价值就是第一个物品的价值,否则价值为0
代码实现:
package com.yang.day02;
import java.util.Scanner;
/**
* 零一背包问题
* 给定一组物品,每种物品都有自己的重量和价格,在限定的总重量内,我们如何选择,
* 才能使得物品的总价格最高。问题的名称来源于如何选择最合适的物品放置于给定背包中。(每个物品只能选择一次)
* @author yang
*
*/
public class ZeroOneBackpack {
public static void main(String[] args) {
Scanner scan = new Scanner(System.in);
// 输入 物品个数
int n = scan.nextInt();
// 输入最大背包容量
int maxContainer = scan.nextInt();
// 输入 每个物品重量 每个物品的价值
int []weight = new int[n];
int []values = new int[n];
for(int i=0; i<n; i++) weight[i] = scan.nextInt();
for(int i=0; i<n; i++) values[i] = scan.nextInt();
// 动态 dp
int [][]dp = new int[n+1][maxContainer+1];
// 初始化dp
for(int i=0; i<=n; i++) {
for(int j=0; j<= maxContainer; j++) {
if(j == 0 || i==0) dp[i][j] = 0; // 背包容量为0时,价值为0,第零个物品,价值任然为零
if(i == 1) { //第一个物品时,如果背包容量大于物品重量,价值就是第一个物品的价值,否则价值为0
if(weight[i]<j) dp[i][j] = 0;
else dp[i][j] = values[i];
}
}
}
// 开始dp动态规划
for(int i=1; i<=n; i++) {
for(int j=1; j<=maxContainer; j++) {
if(j-weight[i-1]>=0) dp[i][j] = Math.max(dp[i-1][j],dp[i-1][j-weight[i-1]]+values[i-1]);
else dp[i][j] = dp[i-1][j];
}
}
System.out.println("最大价值为:"+dp[n][maxContainer]);
}
}