0 1揹包 使用滾動陣列時為何要逆序列舉

2021-07-10 03:01:42 字數 1959 閱讀 1114

問題簡述:有一揹包,最大體積是10,有三個物品,體積分別是3,4,5,重量分別是4,5,6,求在不超過揹包體積的前提下,所放物品的最大重量是多少。

答:最大重量是11,選擇的物品是2和3,其體積是9,小於揹包體積10

我們已經知道,對於0-1揹包問題,我們可以使用動態規劃進行解決。

定義f(i,j):把前i個物品裝入體積為j的揹包中的最大重量

那麼:f(i,j)=max

根據該方程,我們可以用下面的程式解決上述問題:

#include #include #include #include using namespace std;

int main()

; // 體積

int w[4] = ; // 重量

int f[11][11];

memset(f,0,sizeof(f));

// f(i,j):把前i個物品裝到容量是j的揹包中的最大總重量

// f(i,j)=max

// 一定要牢記:我們要計算的是:f(3,10) !!! 什麼意思?對我們來講,11*11的陣列,只有f(3,10)對我們來講是有意義的,其它的空間能省則省。^_^

for (int i=1; i<=3; i++)

} }

// 輸出

for (int i=0; i<=3; i++)

printf("\n"); }

printf("the only answer we need is %d\n",f[3][10]);

}

輸出結果:

對這個答案我們滿意嗎?

如果從最終答案的角度來講,可以接受,畢竟我們得出了想要的解。可是,當資料量很大的時候,比如有m個物品,揹包體積是n,那麼我們需要定義陣列為f[m][n],其空間複雜度為o(m*n)。而誠如我們所說過的那樣,我們僅僅想知道的是揹包能裝下的最大重量(不要求列印方案),即f[n][m]的值,那麼,有必要申請n*m的陣列嗎?或者說,有無方法可以降低空間複雜度呢?答案是有的,即滾動陣列。

觀察上圖,除最後一行外,其餘資料是多餘的.....

由f(i,j)=max,我們可以簡化為f(j)=max

程式如下:

#include #include #include #include using namespace std;

int main()

; // 體積

int w[4] = ; // 重量

int f[11];

memset(f,0,sizeof(f));

// f(j):容量是j的揹包中的最大總重量

// f(j)=max

for (int i=1; i<=3; i++)

}// 輸出

for (int j=0; j<=10; j++)

printf("\n"); }

printf("the only answer we need is %d\n",f[10]);

}

輸出結果:

為什麼計算時要逆序列舉?計算順序所決定!請看下圖:

如當i=1時,f(10)=max=max=max=4

即f(10)依賴的是f(10)和f(7)的值,需要注意的是,此時的f(10)和f(7)是i=0時的f(10)和f(7),如果使用二維陣列儲存,無需擔心覆蓋問題,對體積的列舉逆序或正序都可,但是如果使用一維陣列,若正序列舉會先計算f(7),那麼,再計算f(10)時,i=0時的f(7)已被覆蓋矣!

0 1揹包使用滾動陣列壓縮空間

所謂滾動陣列,目的在於優化空間,從上面的解法我們可以看到,狀態轉移矩陣使用的是乙個n v的陣列,在求解的過程中,我們可以發現,當前狀態只與前一狀態的解有關,那麼之前儲存的狀態資訊已經無用了,可以捨棄的,我們只需要空間儲存當前的狀態和前一狀態,所以只需使用2 v的空間,迴圈滾動使用,就可以達到跟n v...

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 printf d n f ...

揹包九講 01揹包問題(dp 滾動陣列)

現在有n件物品和乙個容量為v的揹包。第i件物品的費用是cost i 價值是value i 每個物品最多只能選一次,求解在不超過揹包容量的限制下,如何選取物品組合能使收益最大化?題型有兩種,一種要求揹包恰好放滿,一種不要求揹包恰好放滿 現在考慮第二種題型,即不要求揹包恰好放滿 將問題分解成子問題 有i...