您的位置:首页 > 其它

动态规划 0-1背包问题和时间轴问题

2017-10-13 22:07 225 查看
背包问题:有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
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息