小偷不可怕,就怕小偷有文化,更怕小偷學過動態規劃。html
白玉湯曾是江湖上赫赫有名的盜聖,奈何歲月不饒人,上了年紀後腿腳便不利索了,無奈一身的本領卻沒有個傳承之人。這天,一位少年前來拜師學藝,但願白玉湯能在偷盜一事上指點一二。白玉湯見這少年骨骼清奇,心裏有收其爲徒的想法,便出了下面這道題考考少年:c++
話說地主金館長家有個專門藏金銀財寶的房間,潛入後發現可偷之物太多,奈何自身負重能力有限,你的包只能承重20kg的物品,並且每一個物品的價值又都不同,那麼問題來了,將哪些物品裝入揹包才能不枉此行,使價值總和最大呢?物品重量和其價值的關係以下:bash
編號 | 重量(w) | 價值(v) |
---|---|---|
1 | 2 | 3 |
2 | 3 | 4 |
3 | 4 | 5 |
4 | 5 | 8 |
5 | 9 | 10 |
少年一看,這不就是一道「01揹包問題嗎」,說完便在地上作出了以下分析:ide
設咱們的揹包裏面的物品價值爲b,給揹包添加兩個參數:k和c,即b(k,c),那麼b(k,c)又表示什麼什麼意思呢?網站
k表示你面對的物品編號,即1~5, c表示你面對k號物品時,揹包的剩餘容量 b(k,c)表示面對k號物品,並做出拿或不拿的選擇以後,揹包裏面的物品總價值編碼
舉個例子,b(2,20)表示的是,在你的揹包容量爲20的狀況下,當你面對2號物品時並做出拿或者不拿的選擇後,揹包中物品的總價值。spa
瞭解了這個概念後咱們繼續: 假設你如今碰見了第k號物品,此時你的揹包容量爲c,你得作出一個決策,到底要不要拿走第k件物品呢?那麼拿不拿的前提是啥?固然是這個物品重不重,能不能塞到包裏。因此第一種狀況就出現了:3d
w[k]>c
。因此此時包中物品的價值就是我拿的前一個物品以後包中的價值,即 b(k,c)=b(k-1,c).
包中剩餘空間不變,仍是c。那麼第二種狀況,若是我拿得動第k件物品,即第k件物品的重量w[k]<c
,面對k號物品,無外乎兩種選擇,拿或者不拿,這時我就要根據拿走以後產生的效益進行決策了:code
b(k,c)=b(k-1,c)
,和第一種拿不動k號物品的同樣。b(k,c)=b(k-1,c-w[k])+v[k]
拿了第k件物品後,那個人包中的價值確定就是原先的價值再加上第k件物品的價值,並且拿了以後包中的剩餘容量就爲c-w[k]
了。 總結一下,就是以下的公式了:b(k,c)=max{b(k-1,c),b(k-1,c-w[k])+v[k]}
剩下的只須要比較小這兩種方式誰的效益大便可。思惟導圖以下: cdn
看懂以上描述後,編碼就很簡單了,這裏我用Java寫出來
class Main {
public static void main(String[] args) {
int[] w = { 0, 2, 3, 4, 5, 9 };
int[] v = { 0, 3, 4, 5, 8, 10 };
int N = 6, W = 21;
int[][] b = new int[N][W];
for (int k = 1; k < N; k++) {
for (int c = 1; c < W; c++) {
if (w[k] > c) {
b[k][c] = b[k - 1][c];
} else {
int value1 = b[k - 1][c - w[k]] + v[k]; // 拿第k件物品
int value2 = b[k - 1][c]; // 不拿第k件物品
b[k][c] = Math.max(value1, value2);
}
}
}
System.out.println(b[5][20]);
}
}
複製代碼
結果爲26
其實公衆號以前有發過一篇相似的「01揹包問題」的解析——《使用動態規劃解決童年難題》,網上大多數的博客在解析「01揹包問題」時也都是採用畫圖的形式,相似於這樣的:
可是我當時看的時候真的是一臉懵逼,而後再帶着圖去看長篇的解析就更混亂了。但願你能在理解了上述流程以後,再回過頭去看公衆號以前的文章,對那個圖的理解應該就會更加深入了,兩者一塊兒看,輔助理解。若是你以爲本文還不錯,麻煩隨手點贊與轉發吧。😋(撲通
點贊與轉發是最好的支持