1、01揹包問題介紹算法
揹包問題是經典的動態規劃問題之一;優化
常見的01揹包問題就是說有一堆物品,如今要裝入一個容器中,這些物品的重量和價值各不一致,而容器的重量又是有限的,沒種物品只能裝1個或者不裝(0個),求當重量限定爲w時,這些物品能裝進去組合成的最高價值是多少?spa
分析:咱們首先將物品排成一排(隨機),依次標記爲1號,2號。。。。而後從一號開始依次往裏放,放的時候判斷當前物品是否是應該放進去:code
若是當前物品放進去以後的最高總價值 比 不放進去的最高總價值 大 ,那麼就是要放入,而後總價值取放入以後的blog
反之不放入,總價值依然取以前的值。get
而且,在對同一個物品判斷時,從0依次增長容器的容量,直到上限wtable
當前物品放進去以後的最高總價值 = 上一號物品判斷時(容量 = 當前容量 - 當前物品重量 時)總價值 + 當前物品的價值class
不放進去的最高總價值 = 上一號物品判斷的時候(容量 = 當前容量 時)總價值容器
因此有僞代碼以下:遍歷
if (當前物品重量 > 當前容量) { 此時最高總價值 = 當前物品不放進去的最高總價值; // 此時物品重量比容量大,放不進去,只能取放不進去的狀況 } else { 此時最高總價值 = max(當前物品不放進去的最高總價值, 當前物品放進去的最高總價值); }
所以,咱們能夠用一個狀態表來對整個過程進行描述。
假設如今是有三個物品 (不是三種)一號、二號、三號,重量分別爲3,2,5; 價值分別爲7,4,8; 當前容器容量爲8,求最大價值。
首先,第一行,爲沒有任何物品的時候,所有置0;
第二行,判斷一號物品能不能放:
當容量擴充爲3的時候,一號物品終於能放,價值爲7,因爲當前也只有一號,因此後面都是7;
第三行,判斷二號物品能不能放:
當容量擴充爲2的時候,二號物品終於能放,價值爲4,與不放進去的最高價值(一號物品在此容量時的值:0)進行比較,因此成功放進去;
當容量擴充爲3的時候,二號物品必定放時,價值爲4,與不放進去的最高價值(一號物品在此容量時的值:7)進行比較,決定不放進去了;
。。。
當容量擴充爲5的時候,二號物品必定放時,最高價值爲(一號物品在(當前容量 - 二號物品的重量)時的價值+二號物品的價值)
=(一號物品在容量爲(5-2)的時候 + 二號物品的價值)= 7 + 4 =11
。。。【以下表】
容量 | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 |
無 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
一號 | 0 | 0 | 0 | 7 | 7 | 7 | 7 | 7 | 7 |
二號 | 0 | 0 | 4 | 7 | 7 | 11 | 11 | 11 | 11 |
三號 | 0 | 0 | 4 | 7 | 7 | 11 | 11 | 12 | 15 |
因此有代碼以下:
public static int getMaxValue(int[] weight, int[] value, int w) { int n = weight.length; int[][] table = new int[n + 1][w + 1]; for (int i = 1; i <= n; i++) { // 物品遍歷(第0行確定全是0,因此不用遍歷) for (int j = 0; j <= w; j++) { // 揹包大小遞增 if (weight[i-1] > j) { // 當前物品i的重量比揹包容量j大,裝不下,只能是不裝 table[i][j] = table[i - 1][j]; } else { // 裝得下,Max{不裝物品i, 裝物品i} table[i][j] = Math.max(table[i - 1][j], table[i - 1][j - weight[i-1]] + value[i-1]); } } } return table[n][w]; }
提問:爲何要【n+1】行?
——由於以後每個數都須要與上一行作比較,一號物品放入的時候也須要與沒放入的時候作比較,因此空出一行做爲「沒有物品的時候」;
爲何要【w+1】列?
——由於在容量遞增時,第一個物品能放的時候,須要與容量減去當前物品的重量,也就是容量爲0的時候作比較,因此,也是須要一列做爲「0容量的時候」;
爲何是weight[i - 1] 和 value[i - 1]?
——由於傳入的重量和價值必然是從一號物品開始,而咱們的表是從「沒有物品」和「0容量」開始,多了一行和一列,因此 i 是當前物品的標號,而當前物品下標爲【標號-1】;
2、空間複雜度優化
很顯然,上面算法的空間複雜度爲矩陣大小【n+1】*【w+1】。
n可能不大,可是實際應用傷的w可能會很大,這樣形成了比較大的空間佔用。
而仔細觀察發現,矩陣中的值只與當前值的左上角的矩陣裏的值有關,如圖二號物品容量爲4時的價值7,只與綠色標註值有關。
容量 | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 |
無 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
一號 | 0 | 0 | 0 | 7 | 7 | 7 | 7 | 7 | 7 |
二號 | 0 | 0 | 4 | 7 | 7 | 11 | 11 | 11 | 11 |
三號 | 0 | 0 | 4 | 7 | 7 | 11 | 11 | 12 | 15 |
而且咱們是按行進行更新的,因此咱們用一個一維數據就能進行狀態的更替,不過要注意更新的方向。
因此依賴關係爲:下面依賴上面,右邊依賴左邊;
若是正常地從左到右邊,那麼右邊面等待更新的值須要的依賴(左邊)就會被覆蓋掉,因此應該每行從右邊開始更新。代碼以下:
public static int getMaxValueByOne(int[] weight, int[] value, int w) { int n = weight.length; int[] table = new int[w + 1]; for (int i = 1; i <= n; i++) { for (int j = w; j >= 0; j--) { if (weight[i-1] <= j) { table[j] = Math.max(table[j], table[j - weight[i-1]] + value[i-1]); } } } return table[w]; }
如此一來,01揹包的空間複雜度就降爲了O(w)。