1. 程式人生 > >01揹包 1維 (滾動陣列)

01揹包 1維 (滾動陣列)

這裡先說一下二維的。

///01揹包
///設物品有n件物品,揹包容量為w
int w[];     ///代表n件物品的價值
int pw[];    ///代表n件物品各佔的容量

int f[n+50][w+50];   ///最優解二維陣列
///f[i][j]陣列 代表存i件物品在容量為j的揹包中得到的價值

void package_01(){
    for(int i=0;i<=n;i++) f[i][0]=0;
    for(int j=0;j<=c;j++) f[0][j]=0;    ///初始化,       ///若要求恰好裝滿,除這兩個初始化外,其他值全部賦值為 -0x3f3f3f3f,(這樣能夠保證最後恰好裝滿)
                                                         ///

    for(int i=1;i<=n;i++){      ///有點列舉的感覺,列舉n件物品
        for(int j=pw[i];j<=c;j++){  ///與上同理
            if(f[i-1][j-pw[i]]+w[i]>f[i-1][j])  ///i-1件物品放入j-pw[i]容量的價值+w[i]的價值(即為放) 與 i-1件物品放入j容量(即為不放) 的所得價值比較
                f[i][j]=f[i-1][j-pw[i]]+w[i];
            else f[i][j]=f[i-1][j];
        }
    }
    printf("%d\n",f[n][w]);     ///f[n][w]即為最優解
}

二維的狀態轉移方程

f[i][j] = max \left\{\begin{matrix} f[i-1][j-pw[i]] + w[i] \\ f[i-1][j] \end{matrix}\right. 

很顯然,f[i][*]  只與  f[j-1][*] 的狀態有關  所以這裡可以有空間上的優化。

先看程式碼:

memset(dp, 0, sizeof(dp));
for(int i=0; i<n; i++){
    for(int j=c; j>=pw[i]; j--){
        dp[j] = max(dp[j], dp[j-pw[i]]+w[i])
    }
}

對於外層的迴圈,每進行一次,dp[] 儲存的狀態都還是  i - 1 時候的dp[] ,所以在第二層迴圈使用的時候就相當於使用的是

dp[i - 1][j - pw[i]] + w[i] 與 dp[i-1][j] ..

並且,狀態轉移方程,每一次推導 f[i][j] 是通過 f[i-1][j-w[i]] 來推導的,所以一維陣列中j的掃描順序應該從大到小(c 到 0),否者前一次迴圈儲存下來的值將會被修改,從而造成錯誤。