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

揹包问题:有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

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章