在动态规划中,背包问题是一个经典问题,因此,在这里对背包问题做一个小小的总结。
背包问题大致概述:
给定一个背包,容量为V,现有N种物品,求出在背包容量范围内,按照某种策略放入,能获得的最大价值是多少
背包问题主要分为三个类型:
(设置dp[i][j]来表示放入第i种物品,当前容量为j时产生的最大价值)
0 1背包问题
对于每种物品,只有一个,要么放进背包,要么不放入
故对于当前dp[i][j],首先判断 weight[i-1]>j(因为i,j都是是从1开始循环,所以这里是i-1,后同) 是否成立,即判断当前物品是不是超过了背包当前的容量
1.若是超过了当前的容量,则无法放入第i种物品
dp[i][j]=dp[i-1][j];
2.若是可以放入,则当前最大价值是不放入与放入第i种物品两种情况之间取最大值
dp[i][j]=Math.max(dp[i-1][j],dp[i-1][j-weight[i-1]]+value[i-1]);
因为放入第i种物品之前,需要预留第i种物品的空间大小,即为weight[i-1]
故放入第i种物品是:dp[i-1][j-weight[i-1]]+value[i-1]
多重背包问题
多重背包即对于每种物品,定义了它的数量为num[i-1]个,所以,和上面的0 1背包问题不同之处在于:对于第i个物品,若是选择放入,即放入的只是一个了,而是多个,至于具体放多少个,要通过计算比较来完成
即因为当前背包的容量,不一定能放下所有的第i种物品,所以需要通过:j/wieght[i-1]计算出当前第i种物品可以放入的数量,再与i物品本来的数量两者之间取最小值即可
即 int maxNum = Math.min(num[i - 1], j / weight[i - 1]);
故可以将第i种物品放入背包时,为:
for (int k = 0; k < maxNum + 1; k++) {
dp[i][j] = Math.max(dp[i - 1][j], dp[i - 1][j - k * weight[i - 1]] + k * value[i - 1]);
}
对于放入多少个我们并不知道,所以使用循环遍历取最大值
完全背包问题
完全背包问题即是:物品的数目不限,可以放入背包能容纳的最大限度
因为是放进任意个物品,注意这里当考虑放入一个物品 i 时应当考虑还可能继续放入 i,
所以下面的放入时的最大值情况是dp[i][j-weight[i-1]]+value[i-1]
dp[i-1][j]表示不放入,,dp[i][j-weight[i-1]]+value[i-1]表示放入第i件物品后还可以继续放入
package demo.lyq;
//动态规划之背包问题
public class DP_Packge {
/**
* 0 1背包问题 每种物品只有一个,要么放进背包,要么不放入
* @param V 背包容量
* @param N 物品种类数
* @param weight 每种物品的重量
* @param value 每种物品的价值
* @return
*/
public static int ZeroPackge(int V,int N,int[] weight,int[] value)
{
//新建一个二维数组dp,用于表示当前状态下的最大价值,dp[i][j]表示在当前放入i件物品,背包容量为j的情况下的最大价值
int[][]dp=new int[N+1][V+1];
for(int i=1;i<N+1;i++)
{
for(int j=1;j<V+1;j++)
{
if(weight[i-1]>j)//对于物品重量大于当前背包容量而放不下的情况,只能是选择不放入这次的物品
{
dp[i][j]=dp[i-1][j];
}
//否则,对于这次的物品选择放入进去,比较与不放入之间的值,取较大的那种情况
//dp[i-1][j]表示不放入,,dp[i-1][j-weight[i-1]]+value[i-1]表示放入第i件物品
dp[i][j]=Math.max(dp[i-1][j],dp[i-1][j-weight[i-1]]+value[i-1]);
}
}
return dp[N][V];
}
/**
* 多重背包问题 每种物品有限定的数量
* @param V 背包容量
* @param N 物品种类数
* @param weight 每种物品的重量
* @param value 每种物品的价值
* @param num 每种物品的最大使用个数
* @return
*/
public static int MultiPackge(int V,int N,int[] weight,int[] value,int[] num)
{
//新建一个二维数组dp,用于表示当前状态下的最大价值,dp[i][j]表示在当前放入i件物品,背包容量为j的情况下的最大价值
int[][]dp=new int[N+1][V+1];
for(int i=1;i<N+1;i++) {
for (int j = 1; j < V + 1; j++) {
if (weight[i - 1] > j)//对于物品重量大于当前背包容量而放不下的情况,只能是选择不放入这次的物品
{
dp[i][j] = dp[i - 1][j];
} else {
//考虑物品的件数限制,maxNum代表当前可以使用的物品的最大数量
// 要么是当前的容量/当前物品的重量计算出来的最大容量
int maxNum = Math.min(num[i - 1], j / weight[i - 1]);
for (int k = 0; k < maxNum + 1; k++) {
dp[i][j] = Math.max(dp[i - 1][j], dp[i - 1][j - k * weight[i - 1]] + k * value[i - 1]);
}
}
}
}
return dp[N][V];
}
/**
* 完全背包问题,对于每种物品,可以放进任意个,只要背包能装下
* @param V 背包容量
* @param N 物品种类数
* @param weight 每种物品的重量
* @param value 每种物品的价值
* @return
*/
public static int CompletePackge(int V,int N,int[] weight,int[] value)
{
//新建一个二维数组dp,用于表示当前状态下的最大价值,dp[i][j]表示在当前放入i件物品,背包容量为j的情况下的最大价值
int[][]dp=new int[N+1][V+1];
for(int i=1;i<N+1;i++)
{
for(int j=1;j<V+1;j++)
{
if(weight[i-1]>j)//对于物品重量大于当前背包容量而放不下的情况,只能是选择不放入这次的物品
{
dp[i][j]=dp[i-1][j];
}
//否则,对于这次的物品选择放入进去,比较与不放入之间的值,取较大的那种情况
//因为是放进任意个物品,注意这里当考虑放入一个物品 i 时应当考虑还可能继续放入 i,
// 所以下面的放入时的最大值情况是dp[i][j-weight[i-1]]+value[i-1]
//dp[i-1][j]表示不放入,,dp[i][j-weight[i-1]]+value[i-1]表示放入第i件物品后还可以继续放入
dp[i][j]=Math.max(dp[i-1][j],dp[i][j-weight[i-1]]+value[i-1]);
}
}
return dp[N][V];
}
}
待补充与简化