跟着ChatGPT學算法-完全揹包問題

一、題目

給定 n 個物品,第 i 個物品的重量爲 wgt[i-1]、價值爲 val[i-1] ,和一個容量爲 cap 的揹包。每個物品可以重複選取,問在限定揹包容量下能放入物品的最大價值。

 

二、和ChatGPT聊聊

您是一位資深算法工程師,請深入淺出地給出完全揹包問題的分析過程和完整帶註釋的Java代碼實現

 

GPT-4-Turbo

 完全揹包問題是動態規劃中的一個經典問題,與0-1揹包問題相似,但在完全揹包問題中,每種物品可以被無限次選取。這個問題的目標是在不超過揹包容量的前提下,最大化揹包中物品的總價值
 

分析過程

  1. 定義狀態:定義 dp[i][c] 爲考慮前 i 個物品,當揹包容量爲 c 時能達到的最大價值。

  2. 狀態轉移方程: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
  3. 初始化:dp[0][...] = 0,表示當沒有物品可選時,任何容量的揹包的最大價值都是0。

  4. 目標: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 }
View Code

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章