01揹包問題

01揹包問題

一:問題

  有$N$件物品和一個容量爲$V$的揹包。第$i$件物品的體積是$C_i$,其價值是$W_i$。求解,在不超過揹包容量狀況下,將哪些物品裝入揹包可以使價值總和最大。php

二:基本思路

  這是最基礎的揹包問題,特色是:每種物品僅有一件。
  狀態 $F[i,v]$表示前$i$件物品中選擇若干件放在容量爲$v$的揹包中,能夠取得的最大價值。
  轉移方程
$$
F[i,v]=\max {F[i−1,v],F[i−1,v−C_i]+W_i}
$$
對於第$i$件物品,有放與不放兩種選擇。若選擇不放,$F[i,v]=F[i−1,v]$;若選擇放,$v−C_i$確保有足夠的空間,隨之$F[i,v]=F[i−1,v−C_i]+W_i$。ios

三:代碼

/**
 *
 * author 劉毅(Limer)
 * date   2017-03-17
 * mode   C++
 */
#include<iostream>
#include<algorithm>
using namespace std;

int main()
{
    const int N = 6;                     //物品個數
    const int V = 10;                    //揹包體積
    int C[N + 1] = { -1,5,6,5,1,19,7 };  //第i個物品的體積(下標從1開始)
    int W[N + 1] = { -1,2,3,1,4,6,5 };   //第i個物品的價值
    int F[N + 1][V + 1] = { 0 };         //狀態

    for (int i = 1; i <= N; i++)  //對於第i個物品
        for (int v = 0; v <= V; v++)
        {
            F[i][v] = F[i - 1][v];  //第i個不放
            if (v - C[i] >= 0 && F[i][v] < F[i - 1][v - C[i]] + W[i])  //若是比它大,再放第i個
                F[i][v] = F[i - 1][v - C[i]] + W[i];
        }

    cout << "最大價值是:" << F[N][V] << endl;  //9

    return 0;
}

四:空間複雜度優化

  以上方法的時間和空間複雜度均爲$O(VN)$,其中時間複雜度應該已經不能再優化了,但空間複雜度卻能夠優化到$O(V)$。
  先考慮上面講的基本思路如何實現,確定是有一個主循環i ← 1 to N,每次算出來二維數組$F[i,v]$的全部值。那麼,若是隻用一個數組$F[v]$能不能保證第$i$次循環結束後$F[v]$中表示的就是咱們定義的狀態$F[i,v]$呢?
  $F[i,v]$是由$F[i−1,v]$和$F[i−1,v−C_i]$兩個子問題遞推而來,可否保證在推$F[i,v]$時(也即在第$i$次主循環中推$F[v]$時)可以取用$F[i−1,v]$和$F[i−1,v−C_i]$的值呢?
  事實上,這要求在每次主循環中咱們以v ← V to C[i]的遞減順序計算$F[v]$,這樣才能保證計算$F[v]$時$F[v−C_i]$保存的是狀態$F[i−1,v−C_i]$的值。
  優化後的代碼以下:c++

/**
 *
 * author 劉毅(Limer)
 * date   2017-03-17
 * mode   C++
 */
#include<iostream>
#include<algorithm>
using namespace std;

int main()
{
    const int N = 6;                     //物品個數
    const int V = 10;                    //揹包體積
    int C[N + 1] = { -1,5,6,5,1,19,7 };  //第i個物品的體積(下標從1開始)
    int W[N + 1] = { -1,2,3,1,4,6,5 };   //第i個物品的價值
    int F[V + 1] = { 0 };                //狀態

    for (int i = 1; i <= N; i++)  //對於第i個物品
        for (int v = V; v >= C[i]; v--)
            F[v] = max(F[v], F[v - C[i]] + W[i]);
        
    cout << "最大價值是:" << F[V] << endl;  //9

    return 0;
}

五:初始化的細節問題

  咱們看到的求最優解的揹包問題題目中,事實上有兩種不太相同的問法。有的題目要求「剛好裝滿揹包」時的最優解,有的題目則並無要求必須把揹包裝滿。這兩種問法的實現方法只是在初始化的時候有所不一樣。
  若是是第一種問法,要求剛好裝滿揹包,那麼在初始化時除了F[0]爲0,其它F[1]...F[V]均設爲−∞,這樣就能夠保證最終獲得的F[V]是一種剛好裝滿揹包的最優解。若是並無要求必須把揹包裝滿,而是隻但願價格儘可能大,初始化時應該將F[0]...F[V]所有設爲0。
  這是爲何呢?能夠這樣理解:初始化的F數組事實上就是在沒有任何物品能夠放入揹包時的合法狀態。若是要求揹包剛好裝滿,那麼此時只有容量爲0的揹包能夠在什麼也不裝且價值爲0的狀況下被「剛好裝滿」,其它容量的揹包均沒有合法的解,屬於未定義的狀態,應該被賦值爲-∞了。若是揹包並不是必須被裝滿,那麼任何容量的揹包都有一個合法解「什麼都不裝」,這個解的價值爲0,因此初始時狀態的值也就所有爲0了。數組

參考文獻:
[ 1 ] .揹包九講.優化

文章轉自個人我的博客:http://www.61mon.com/index.php/archives/188/spa

相關文章
相關標籤/搜索