1. 程式人生 > 其它 >動態規劃演算法解決揹包問題

動態規劃演算法解決揹包問題

技術標籤:演算法與資料結構動態規劃揹包問題01揹包資料結構java

一、動態規劃演算法概述

動態規劃演算法與分治法類似,其基本思想也是將待求解問題分解成若干個子問題,先求解子問題,然後從子問題解得到原問題解。

在這裡插入圖片描述

但是經分解得到的子問題往往不是互相獨立的。不同子問題的數目常常只有多項式量級。在用分治法求解極值問題時,有些子問題被重複計算了許多次。

在這裡插入圖片描述

如果能夠儲存已解決的子問題的答案,而在需要時再找出已求得的答案,就可以避免大量重複計算,從而得到多項式時間演算法。

在這裡插入圖片描述

動態規劃 VS 分治法

  • 相同點:基本思想均是將原問題分解成若干個子問題,先求子問題,然後從子問題的解得到原問題的解;
  • 不同點:
    • 適用於動態規劃求解的問題,分解得到的子問題往往不是互相獨立的;
    • 動態規劃為自底向上,而分治法為自頂向下。

當分解出的子問題不相互獨立的話,若使用分治法來求解此類問題,會導致使用指數級的執行時間,而子問題的個數只有多項式量級。所以,此類問題不適合使用分治法。

動態規劃是如何減少對重複子問題求解的呢?

答案是儲存已求解的子問題的解,在需要時找出,從而避免大量的重複計算得到多項式的執行時間;通常用一個表來記錄所有已解決的子問題。

動態規劃演算法通常用於求解具有某種最優性質的問題

二、揹包問題

2.1 什麼是揹包問題

今年的年會,聰明大方的老闆為了激勵員工,決定買三種類型的獎品獎勵給勤奮的員工小王。

在這裡插入圖片描述

為了讓自己不至於放血太多又不會顯得太摳門,美其名曰考驗員工小王的智商。機智的老闆想到了一個好法子:給小王發一個最大能裝下 4 磅物品的揹包,只有裝到揹包中的物品才歸屬於小王。

如果你是員工小王,怎麼裝才能得到最大的物品價值呢?

2.2 揹包問題思路分析

首先,最容易想到的方法,就是計算出各種可能的獎品組合的情況,找出價值最高的組合放入到揹包中。對於三種獎品,共有 8 種的組合,對於四種獎品,共有 16 種組合。每增加一種獎品,需要計算的組合數都將翻倍。如果明年老闆決定獎勵十一種獎品給小王,那小王最好還是放棄獎品算了。我們可以看到,這種演算法的時間複雜度為 O(2ⁿ),效率較低。

如何找到最優解呢?答案是使用動態規劃

對於揹包問題,可以先解決小揹包(子揹包)的問題,然後逐步解決原來的問題。

在這裡插入圖片描述

每個動態規劃演算法都從一個網格開始,揹包問題的網格如下。

在這裡插入圖片描述

網格的各行為獎品,各列為不同容量(1~4磅)的揹包。所有這些列都是必要的,因為它們將用於計運算元揹包中的物品價值。

第一步:

我們首先假設可選擇的獎品只有吉他(1 磅)。當揹包的容量為 1 時,剛好放下一個吉他,此時揹包價值為 1500。當揹包的容量為 2、3、4 時,由於可選的商品只有吉他且同樣的獎品只能拿一件,因此即便會使揹包的空間浪費,揹包中仍然是隻能放一個吉他,揹包中的價值仍然為 1500。

在這裡插入圖片描述

第二步:

我們再假設可選的商品只有吉他(1 磅)和音響(4 磅)。當揹包的容量為 1、2、3 時,由於音響的重量為 4,因此無法放入到揹包中,揹包中只能放入一個吉他。

在這裡插入圖片描述

當揹包的容量為 4 時,剛好等於音響的重量。這個時候有兩個選擇:一是隻放入一個吉他;二是隻放入一個音響。由於音響的價值為 3000,大於吉他的價值,因此選擇將音響放入揹包,此時揹包中的價值為 3000。

在這裡插入圖片描述

第三步:

我們在假設可選擇商品有吉他(1 磅)、音響(4 磅)、膝上型電腦(3 磅)。當揹包的容量為 1、2 時,毫無疑問,揹包中只能放一個吉他。此時揹包的價值為 1500。

在這裡插入圖片描述

當揹包的容量為 3 時,有兩種選擇:一是隻放入一個吉他;二是隻放入一個電腦。由於電腦的價值高於吉他,因此將電腦放入。此時揹包的價值為 2000。

在這裡插入圖片描述

當揹包的容量為 4 時,仍然有兩種選擇:一是放入一個吉他和電腦;二是隻放入一個音響。由於吉他和電腦的總價值高於音響的價值,因此將吉他和電腦放入。此時揹包的價值為 1500 + 2000 = 3500。

在這裡插入圖片描述

因此,將吉他和膝上型電腦裝入揹包時價值最高,這就是小王的最優解。

總結:

那麼這個過程,如何用數學公式表達呢?

我們首先約定一共有 N 件物品,第 i 件物品的重量為 w[i],價值為 v[i],揹包的承載上限為 W。再約定一個狀態 cell[i][j] 表示將前 i 件(這裡的前 i 件指的是有 i 件可選)物品裝進限重為 j 的揹包可以獲得的最大價值(0<=i<=N0<=j<=W)。

那麼我們可以將 cell[0][0...W] 初始化為 0,表示將前 0 個物品裝入書包的最大價值為 0。

i>0 時,cell[i][j] 有兩種情況:

  1. 不裝入第 i 件物品,則揹包最大價值就是相同揹包容量下只有 i-1 件物品可選時的最大價值,即 cell[i-1][j]
  2. 裝入第 i 件物品(若揹包容量夠),則最大價值為當前物品價值加揹包剩餘容量的最大價值,即 v[i] + cell[i-1][j-w[i]]

那麼 cell[i][j] 的值,就是這兩種情況下的最大值。由此可以得到方程:
c e l l [ i ] [ j ] = m a x { c e l l [ i − 1 ] [ j ] c e l l [ i − 1 ] [ j − w [ i ] ] + v [ i ] j > = w [ i ] cell[i][j] = max \left \{\begin {aligned}&cell[i-1][j]\\&cell[i-1][j-w[i]] + v[i]\end{aligned}\right. \quad\quad j>=w[i] cell[i][j]=max{cell[i1][j]cell[i1][jw[i]]+v[i]j>=w[i]
求得所有的 cell[i][j] 之後,最後一個值就是揹包問題的最大值。

2.3 揹包問題程式碼實現

public static void main(String[] args) {
    int[] w = {0, 1, 4, 3};    // 物品的重量
    int[] v = {0, 1500, 3000, 2000};    // 物品的價值

    int num = w.length; // 物品的數量可能的個數 (0~3,共 3 個)
    int weight = 5; // 揹包的容量可能的個數 (0~4,共 5 個)

    int[][] cell = new int[num][weight];    // cell[i][j] 表示前i個物品可選,揹包容量為j時的最大價值
    int[][] record = new int[num][weight];  // record[i][j] 用於標記第i個物品有沒有放入容量為j的揹包

    for (int i=1; i<num; i++){  // 遍歷物品
        for (int j=1; j<weight; j++){   // 遍歷揹包容量
            if (w[i]>j){    // 如果當前物品重量大於揹包容量
                cell[i][j] = cell[i-1][j];  // 不裝入
            }else{  // 如果當前物品重量小於等於揹包容量
                int value_1 = cell[i-1][j]; // 不放入第 i 個物品的揹包價值
                int value_2 = v[i] + cell[i-1][j-w[i]]; // 放入第 i 個物品後的價值
                /* 把最大價值放入揹包 */
                if (value_1 > value_2){
                    cell[i][j] = value_1;
                }else{
                    cell[i][j] = value_2;
                    record[i][j] = 1;   // 用於標記在本網格放入了第 i 個物品
                }
            }
        }
    }

    // 最後一個網格就是最大價值
    System.out.println("揹包價值為:" + cell[num-1][weight-1]);

    // 由於最後一個網格就是最大價值
    // 因此,逆序遍歷 record 找到最後一個放入的物品,然後找到剩餘空間價值是放入第幾個物品
    int i = record.length-1;
    int j = record[0].length-1;
    while (i > 0 && j > 0){
        if (record[i][j] == 1){
            System.out.printf("放入了第 %d 個商品\n", i);
            j = j - w[i];   // 揹包剩餘容量
        }
        i--;
    }
}

執行結果如下:

在這裡插入圖片描述
【注】:本文的圖大多出自《演算法圖解》。