01背包问题
题目
假设有一个背包,它可以装重量为8的物品,给出4个物品,问,如何装才能使得这个背包装的物品的总价值最大?背包到底装入了哪些物品?
思路
利用动态规划来做,我们,要开一个dp数组,数组的行数与物品的个数+1相等,数组的列数与背包的能装的最大价值+1相等。
在搜索的时候,行数表示物品的编号,列数表示当前背包的体积。
我们只需要首先考虑,该物品能不能够放进去背包,那么就是当前背包的能装的体积要大于当前要考虑的物品的体积【注意:这里是拿一个空的背包去装,与前面是否装进去物品没有关系】,假设不能够装进去当前物品,那么,当前的值等于在同等背包体积的情况下n-1个物品的所能装入的最大价值。即
dp[i][j]=dp[i-1][j];
假设能够装进去该物品,那么就要考虑装不装该物品,假设不装,那么值就是在同等体积的背包下前n-1个物品的最大价值,如果能够装进去,那么,当前位置的最大价值就是给当前物品预留了体积之后,在剩余价值里,对于前n-1个物品而言,所能取得的最大家价值之和。
在以上的值中取出一个大的即可。
完整的动态规划代码:
dp[i][j]=Math.max(dp[i-1][j],dp[i-1][j-weight[i]]+value[i]);
public static void work(){
for (int i=1;i<5;++i)
{
for (int j=1;j<9;++j)
{
//判断,即拿一个当前体积的空背包,判断当前物品是否能够放进去背包
if (weight[i]>j) //不能放进去
{
//在当前背包体积的情况下,前面n-1个物品的最大和组合就是当前n个物品的最大和组合
dp[i][j]=dp[i-1][j];
}
else //当前物品可以装入背包,但不一定要放进去
{
//可以放,但不放,取前面情况;可以放,也放,要先预留出对应的位置,
//取出n-1个物品在当前预留完位置之后的最大价值组合并且加上当前物品的组合
//再去取最大值
dp[i][j]=Math.max(dp[i-1][j],dp[i-1][j-weight[i]]+value[i]);
}
}
}
}
该背包到底装了哪些物品呢?
我们需要从答案的位置进行回溯,假设在背包体积相同的情况下,n个物品所能取得的最大价值与n-1个物品的最大价值一致,那么就表示当前物品没有放到背包中。这个时候,就需要回溯n-1个物品的对应的体积的情况,
假设在同等体积的背包情况下,当前n个物品所取得的最大价值与前n-1个物品所取得的最大价值不一致【即当前物品是在预留了足够的体积后所取得的当前物品的价值与剩下n-1个物品在剩下的体积重所取得的最大价值之和】,那么就表示该物品进入到了背包之中。
//回溯到底装了哪些物品
public static void find(int i,int j)
{
if (i==0) //当搜寻到第0行的时候,结束回溯
{
for (int g=0;g<5;++g)
{
System.out.print(object[g]+" ");
}
return;
}
if (dp[i][j]==dp[i-1][j])
{
object[i]=0;//即在背包体积固定的情况下,对于前n个物品和前n-1个物品,
// 背包的最大价值没有变化,即该物品没有放入背包
find(i-1,j);
}
//当前物品被装入时,对应位置的最大价值得到验证
else if (dp[i][j]==dp[i-1][j-weight[i]]+value[i])
{
object[i]=1;
find(i-1,j-weight[i]);//找寻去除掉当前的物品的价值剩余价值并且对应的n-1个物品的最大价值组合
}
}
整道题的代码:
//背包的最大价值为8
public class Main {
static int weight[]={0,2,3,4,5};//重量数组
static int value[]={0,3,4,5,6};//价值数组
//行数与物品的个数有关,列数与背包的最大容量有关
static int dp[][]=new int[5][9];
static int object[]=new int[5];//结果数组
public static void main(String[] args) {
work();
}
public static void work(){
for (int i=1;i<5;++i)
{
for (int j=1;j<9;++j)
{
//判断,即拿一个当前体积的空背包,判断当前物品是否能够放进去背包
if (weight[i]>j) //不能放进去
{
//在当前背包体积的情况下,前面n-1个物品的最大和组合就是当前n个物品的最大和组合
dp[i][j]=dp[i-1][j];
}
else //当前物品可以装入背包,但不一定要放进去
{
//可以放,但不放,取前面情况;可以放,也放,要先预留出对应的位置,
//取出n-1个物品在当前预留完位置之后的最大价值组合并且加上当前物品的组合
//再去取最大值
dp[i][j]=Math.max(dp[i-1][j],dp[i-1][j-weight[i]]+value[i]);
}
}
}
//行数减1.列数减1
System.out.println(dp[4][8]);
find(4,8);
}
//回溯到底装了哪些物品
public static void find(int i,int j)
{
if (i==0) //当搜寻到第0行的时候,结束回溯
{
for (int g=0;g<5;++g)
{
System.out.print(object[g]+" ");
}
return;
}
if (dp[i][j]==dp[i-1][j])
{
object[i]=0;//即在背包体积固定的情况下,对于前n个物品和前n-1个物品,
// 背包的最大价值没有变化,即该物品没有放入背包
find(i-1,j);
}
//当前物品被装入时,对应位置的最大价值得到验证
else if (dp[i][j]==dp[i-1][j-weight[i]]+value[i])
{
object[i]=1;
find(i-1,j-weight[i]);//找寻去除掉当前的物品的价值剩余价值并且对应的n-1个物品的最大价值组合
}
}
}