一、題目
給定 n 個物品,第 i 個物品的重量爲 wgt[i-1]、價值爲 val[i-1] ,和一個容量爲 cap 的揹包。每個物品可以重複選取,問在限定揹包容量下能放入物品的最大價值。
二、和ChatGPT聊聊
您
您是一位資深算法工程師,請深入淺出地給出完全揹包問題的分析過程和完整帶註釋的Java代碼實現
GPT-4-Turbo
完全揹包問題是動態規劃中的一個經典問題,與0-1揹包問題相似,但在完全揹包問題中,每種物品可以被無限次選取。這個問題的目標是在不超過揹包容量的前提下,最大化揹包中物品的總價值分析過程
定義狀態:定義
dp[i][c]
爲考慮前i
個物品,當揹包容量爲c
時能達到的最大價值。狀態轉移方程:dp[i][c] = max(dp[i-1][c], dp[i][c-wgt[i-1]] + val[i-1])
- 如果不選當前物品
i
,則dp[i][c] = dp[i-1][c]
,直接繼承前一個狀態的值。- 如果選當前物品
i
,則dp[i][c] = dp[i][c-wgt[i]] + val[i]
,表示在當前揹包容量減去物品i
的重量後的最大價值基礎上加上物品i
的價值。由於物品可以被無限次選取,我們仍然在考慮物品i
。初始化:
dp[0][...] = 0
,表示當沒有物品可選時,任何容量的揹包的最大價值都是0。目標:
dp[n][cap]
,表示考慮所有n
個物品,當揹包容量爲cap
時能達到的最大價值。
您
那爲什麼轉移方程不是 dp[i][c] = max(dp[i-1][c], max(dp[i-1][c-wgt[i-1]] + val[i-1],dp[i][c-wgt[i-1]] + val[i-1]))
GPT-4-Turbo
您提出的狀態轉移方程: [dp[i][c] = max(dp[i-1][c], max(dp[i-1][c-wgt[i-1]] + val[i-1], dp[i][c-wgt[i-1]] + val[i-1]))]
實際上包含了冗餘的比較。這是因爲,當我們在解決完全揹包問題時,對於每個物品,我們有兩個選擇:不選這個物品,或者選這個物品。如果選擇不選這個物品,我們就直接繼承了前一個狀態的值,即
dp[i-1][c]
。如果選擇選這個物品,我們需要考慮的是在當前物品可以被無限選取的情況下,選擇這個物品後的最優解,即dp[i][c-wgt[i-1]] + val[i-1]
。在這裏,
dp[i][c-wgt[i-1]] + val[i-1]
已經隱含了dp[i-1][c-wgt[i-1]] + val[i-1]
的情況。這是因爲dp[i][c-wgt[i-1]]
已經考慮了所有包括第 i 個物品在內的選擇,這自然包括了從不選擇第 i 個物品到選擇多次第 i 個物品的所有情況。因此,dp[i][c-wgt[i-1]] + val[i-1]
實際上已經覆蓋了dp[i-1][c-wgt[i-1]] + val[i-1]
的情況,使得後者成爲前者的一個子情況。因此,原始的狀態轉移方程: [dp[i][c] = \max(dp[i-1][c], dp[i][c-wgt[i-1]] + val[i-1])]
已經足夠覆蓋所有情況,無需再進行冗餘的比較。這個方程簡潔地表達了完全揹包問題的核心思想:對於每個物品,我們可以選擇不選它,或者在選它的情況下,考慮包括它自身在內的所有可能性。
三、GPT-4-Turbod的java實現
1 public class CompleteKnapsack { 2 3 /** 4 * 解決完全揹包問題的函數 5 * @param wgt 物品重量數組 6 * @param val 物品價值數組 7 * @param cap 揹包容量 8 * @return 揹包能裝入物品的最大價值 9 */ 10 public static int completeKnapsack(int[] wgt, int[] val, int cap) { 11 int n = wgt.length; // 物品的數量 12 int[][] dp = new int[n + 1][cap + 1]; // dp數組,多出的一行一列是爲了方便處理邊界情況 13 14 // 初始化dp數組,Java默認初始化爲0,所以這裏可以省略 15 16 // 遍歷所有物品 17 for (int i = 1; i <= n; i++) { 18 // 遍歷所有可能的揹包容量 19 for (int c = 0; c <= cap; c++) { 20 // 不選當前物品i 21 dp[i][c] = dp[i - 1][c]; 22 // 選當前物品i,前提是揹包容量足夠 23 if (c >= wgt[i - 1]) { 24 dp[i][c] = Math.max(dp[i][c], dp[i][c - wgt[i - 1]] + val[i - 1]); 25 } 26 } 27 } 28 29 // 返回考慮所有物品,揹包容量爲cap時的最大價值 30 return dp[n][cap]; 31 } 32 33 public static void main(String[] args) { 34 int[] wgt = {1, 3, 4}; // 物品重量 35 int[] val = {15, 20, 30}; // 物品價值 36 int cap = 4; // 揹包容量 37 38 System.out.println("最大價值: " + completeKnapsack(wgt, val, cap)); 39 } 40 }