揹包问题:有N件物品和一个承受重量为c的揹包。第i件物品的费用是v[i],重量是w[i]。求解将哪些物品装入揹包可使这些物品的费用总和不超过揹包容量,且价值总和最大。
基本思路:
这是最基础的揹包问题,特点是:每种物品仅有一件,可以选择放或不放
假设maxValue[i][j]表示前i件物品恰放入此时承重为j的揹包可以获得的最大价值。那么容易得到状态转移方程是:
maxValue[i][j]=Max{maxValue[i-1][j],maxValue[i-1][j-weight[i]]+value[i]}
maxValue[i-1][j]表示此时揹包放不下物品i
maxValue[i-1][j-weight[i]]+value[i] 可以放下i物品,需要退回到i-1时刻来添加i的价值
int w[]={0,4,5,6,2,2};//第一位是0,是为了方便从1开始计算
int value[]={0,6,4,5,3,6};
int a[][]=new int[6][11];
打表:图片引用其他博主http://blog.csdn.net/mu399/article/details/7722810
这里从左到右边,有底向上开始
先是按行来计算
e这一行,e的重量为4,所以在揹包重量为j=1,2,3时候都是价值为0
如下图例 从上到下开始计算
代码是用第一个图中数据,是从上向下开始的 abe被最终选择
/**
*
* @param maxValue maxValue[i][j]表示在承重为j的包中放的前i个物品的最大价值
* @param maxWeight 揹包的最大承重
* @param goods_number 商品的个数
* @param weight 商品的重量数组
* @param value 商品的价值数组
*/
public void GetValue(int maxValue[][], int maxWeight,int goods_number,int weight[],int value[]){
for(int i=1;i<=goods_number;i++){
for(int j=1;j<=maxWeight;j++){
if(j>=weight[i]){//如果此时揹包的承重>=商品的重量
if(maxValue[i-1][j]>maxValue[i-1][j-weight[i]]+value[i])
maxValue[i][j]=maxValue[i-1][j];//不装第i个物品
else{
maxValue[i][j]=maxValue[i-1][j-weight[i]]+value[i];//把第i个装进去
// a1[i]=1;
}
}
else{
maxValue[i][j]=maxValue[i-1][j];
}
}
}
System.out.println("最大价值:"+maxValue[goods_number][maxWeight]);
//prints(maxValue);
}
上面的直接得到了最大值,如何得到选了哪些商品
最优解即是maxValue(number,weight)=maxValue(5,10)=15,但还不知道解由哪些商品组成,故要根据最优解回溯找出解的组成,根据填表的原理可以有如下的寻解方式:
1) maxValue(i,j)=maxValue(i-1,j)时,说明没有选择第i 个商品,则回到maxValue(i-1,j);
2)maxValue[i][j]=maxValue[i-1][j-weight[i]]+value[i],说明装了第i个商品 输出i,该商品是最优解组成的一部分,随后我们得回到装该商品之前,即回到maxValue(i-1,j-w(i));
3) 一直遍历到i=0结束为止,所有解的组成都会找到。
代码
//开始调用 i=5,j=10
void FindWhat(int i,int j)//寻找解的组成方式
{
if(i>0)
{
if(maxValue[i][j]==maxValue[i-1][j])//相等说明没装
{
FindWhat(i-1,j);
}
else if( j-w[i]>=0 && maxValue[i][j]==maxValue[i-1][j-weight[i]]+value[i] )
{
System.out.println(i);//这里是逆序输出所得到的商品序号
FindWhat(i-1,j-weight[i]);//回到装包之前的位置
}
}
}
空间优化问题:下面的一些表示被简写 weight=w value=v V=maxVlaue
上面的算法的时间o(numbers*揹包承重),空间也是这个,因为用了一个maxValue[i][j]数组
这里用二维实际上是为了好得到哪些商品是被最终装进包中
可以只用一维数组
、
l) 空间优化,每一次V(i)(j)改变的值只与V(i-1)(x) {x:1…j}有关,V(i-1)(x)是前一次i循环保存下来的值;
因此,可以将V缩减成一维数组,从而达到优化空间的目的,状态转移方程转换为 B(j)= max{B(j), B(j-w(i))+v(i)};
并且,状态转移方程,每一次推导V(i)(j)是通过V(i-1)(j-w(i))来推导的,所以一维数组中j的扫描顺序应该从大到小(capacity到0),否者前一次循环保存下来的值将会被修改,从而造成错误。
//空间优化为o(n)
public void GetValue2(int maxValue[], int maxWeight,int goods_number,int weight[],int value[]){
for(int i=1;i<=goods_number;i++){
for(int j=maxWeight;j>=0;j--){//必须是要从大到小
if(j>=weight[i]){//如果此时揹包的承重>=商品的重量
if(maxValue[j]>maxValue[j-weight[i]]+value[i])
maxValue[j]=maxValue[j];//不装第i个物品
else{
maxValue[j]=maxValue[j-weight[i]]+value[i];//把第i个装进去
// a1[i]=1;
}
}
else{
maxValue[j]=maxValue[j];
}
}
}
System.out.println("最大价值:"+maxValue[maxWeight]);
//prints(maxValue);
}
一维数组因为每次会进行覆盖,所以得到不到具体是哪个商品被装进口袋里
图例:
第一组出来因为放不下之外都是第一个物品的价值
第二组 b[10]=Max{b[10]=6,b[10-w[2]]+v[2]=b[10-2]+3=6+3=9} 按照规则排序
时间轴问题 看这个博客
http://blog.csdn.net/hongchh/article/details/52914507