動態規劃 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

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