最近在研究動態規劃算法,剛好看到揹包問題。看到網上講解這方面問題很多,感覺都有點不明白,後面細思苦想好久,終於理解這個思路。於是寫下個人見解以及解題思路。
揹包問題描述如下:
有編號分別爲a,b,c,d,e的五件物品,它們的重量分別是4,2,6,5,3,它們的價值分別是6,3,5,4,6,現在給你個承重爲10的揹包,如何讓揹包裏裝入的物品具有最大的價值總和?
對於這題我們先不解題。先扯扯動態規劃,下面描述的不太詳細,可以自行百度:
動態規劃所處理的問題是一個多階段決策問題,一般由初始狀態開始,通過對中間階段決策的選擇,達到結束狀態。這些決策形成了一個決策序列,同時確定了完成整個過程的一條活動路線(通常是求最優的活動路線)。動態規劃的設計都有着一定的模式,一般要經歷以下幾個步驟。
初始狀態→│決策1│→│決策2│→…→│決策n│→結束狀態
解題公式:f(n,m)=max{f(n-1,m), f(n-1,m-w[n])+P(n,m)}
這個一開始肯定是懵懂的,不要被這些公式和概念嚇到。現在我們開始解題:
1.首先是初始狀態。 假如只有a物品假如包重量爲1-10分別能裝多大價值物品
物品 | 重量 | 價值 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 |
a | 4 | 6 | 0 | 0 | 0 | 6 | 6 | 6 | 6 | 6 | 6 | 6 |
因爲此時只算上了一個a物品,包各個重量的最大價值已經算出來了。這就是動態規劃的初始狀態。
2. 中間各種決策。上一步得到了只有a物品的情況下包各個重量下的最大價值,現在我們來決策1,就在第一步基礎上增加b物件,現在我們有2個物品,再看看包各個重量的最大價值
物品 | 重量 | 價值 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 |
a | 4 | 6 | 0 | 0 | 0 | 6 | 6 | 6 | 6 | 6 | 6 | 6 |
b | 2 | 3 | 0 | 3 | 3 | 6 | 6 | 9 | 9 | 9 | 9 | 9 |
這裏b4詳細計算的。根據上面2個公式我只用取2個值的最大值。這2個值分別爲a4和a(4-2(b的重量))+3(b的價值)。計算後分別爲6和3,我們取最大值6。這步是所有理解的關鍵。
於是我們有決策2(同樣的方式將c物品放進來計算),決策3(將d物品放進來),決策4(將e物品放進來)
3.走完所有決策後,於是我們有最終方案了,包各個重量的最大價值即爲最後將e物品放進來的那一行。
理論說完我們直接上代碼,這裏是php版本代碼,看了下網上例子,幾乎很少有php版本的代碼,感覺有點歧視php。作爲多年php開發經驗,再加上php畢竟世界上最好的語言,哈哈。開個玩笑!
<?php //初始化各個物品重量和價值數組 $weight_arr = array(0,4,2,6,5,3); $value_arr = array(0,6,3,5,4,6); //設置包最大重量爲10 $bag_max = 10; $items = count($weight_arr) - 1; $cache_map = array(); $cache_map[] = array_fill(1, $items, 0); $ik = 1; //初始狀態,假如只有第一個物品包各個重量的最大價值 for($i = 1; $i <= $bag_max; $i++) { if($weight_arr[$ik] > $i) { $cache_map[$ik][$i] = 0; } else { $cache_map[$ik][$i] = $value_arr[$ik]; } } //中間的各種決策(依次放入物品b,c,d,e) for($i = 1; $i <= $bag_max; $i++) for($j = 2; $j <= $items; $j++) { $j_weight = $weight_arr[$j]; $j_value = $value_arr[$j]; $j_prev_value = $cache_map[$j-1][$i]; if($j_weight > $i) { $cache_map[$j][$i] = $j_prev_value; } else { $j_extra_value = $j_value + $cache_map[$j-1][$i-$j_weight]; $cache_map[$j][$i] = max($j_prev_value, $j_extra_value); } } //最終狀態 for($i = 1; $i <= $bag_max; $i++) { echo $i . ':' . $cache_map[$items][$i] . PHP_EOL; }
最後顯示下計算結果吧:
1:0 2:3 3:6 4:6 5:9 6:9 7:12 8:12 9:15 10:15