1. 程式人生 > >動態規劃之01背包問題(含代碼C)

動態規劃之01背包問題(含代碼C)

bsp sys 最優解 ret 時間復雜度 維數 style 時間 沒有

1.動態規劃的基本思想

  動態規劃算法通常用於求解具有某種最優性質的問題。其基本思想也是將待求解問題分解成若幹個子問題,先求解子問題,然後從這些子問題的解得到原問題的解。與分治法不同的是,適合於用動態規劃求解的問題,經分解得到子問題往往不是互相獨立的。若用分治法來解這類問題,則分解得到的子問題數目太多,有些子問題被重復計算了很多次。如果我們能夠保存已解決的子問題的答案,而在需要時再找出已求得的答案,這樣就可以避免大量的重復計算,節省時間。我們可以用一個表來記錄所有已解的子問題的答案。不管該子問題以後是否被用到,只要它被計算過,就將其結果填入表中。這就是動態規劃法的基本思路。具體的動態規劃算法多種多樣,但它們具有相同的填表格式。 與分治法最大的差別是:適合於用動態規劃法求解的問題,經分解後得到的子問題往往不是互相獨立的(即下一個子階段的求解是建立在上一個子階段的解的基礎上,進行進一步的求解)

2.動態規劃的應用場景

  適用動態規劃的問題必須滿足最優化原理、無後效性和重疊性。

  a.最優化原理(最優子結構性質) 最優化原理可這樣闡述:一個最優化策略具有這樣的性質,不論過去狀態和決策如何,對前面的決策所形成的狀態而言,余下的諸決策必須構成最優策略。簡而言之,一個最優化策略的子策略總是最優的。一個問題滿足最優化原理又稱其具有最優子結構性質。

  b.無後效性 將各階段按照一定的次序排列好之後,對於某個給定的階段狀態,它以前各階段的狀態無法直接影響它未來的決策,而只能通過當前的這個狀態。換句話說,每個狀態都是過去歷史的一個完整總結。這就是無後向性,又稱為無後效性。

  c.子問題的重疊性動態規劃將原來具有指數級時間復雜度的搜索算法改進成了具有多項式時間復雜度的算法。其中的關鍵在於解決冗余,這是動態規劃算法的根本目的。動態規劃實質上是一種以空間換時間的算法,它在實現的過程中,不得不存儲產生過程中的各種狀態,所以它的空間復雜度要大於其它的算法。

3.01背包問題建模:如下圖

技術分享

  圖片中abc三個公式詳細解析:a式表示前??個物品中挑選放入承重為0的背包中和沒有物品放入承重為??的背包中是相等為0。 b式表明:如果第??個物品的重量大於背包的容量,則裝人前??個物品得到的最大價值和裝入前???1個物品得到的最大價是相同的,即物品??不能裝入背包。 c式表明:如果第??個物品的重量小於背包的容量,則會有一下兩種情況: (1)如果把第??個物品裝入背包,則背包物品的價值等於第???1個物品裝入容量位?????_?? 的背包中的價值加上第??個物品的價值??_??; (2)如果第??個物品沒有裝入背包,則背包中物品價值就等於把前???1個物品裝入容量為??的背包中所取得的價值。顯然,取二者中價值最大的作為把前??個物品裝入容量為??的背包中的最優解。

4.動態規劃的時間效率為O(nc)其中n表示物品的個數,c表示背包的容量。空間的效率就是用於存儲二維數組的占用空間大小,即為O(nc).

5.代碼如下所示:

#include<stdio.h>
#include<cstdlib>
int V[200][200];//前i個物品裝入容量為j的背包中獲得的最大價值
int max(int a, int b)
{
    if (a >= b)
        return a;
    else return b;
}

int KnapSack(int n, int w[], int v[], int x[], int C)
{
    int i, j;
    for (i = 0; i <= n; i++)
        V[i][0] = 0;
    for (j = 0; j <= C; j++)
        V[0][j] = 0;
    for (i = 0; i < n; i++){
        for (j = 0; j < C+1; j++){
            if (j<w[i])
                V[i][j] = V[i - 1][j];
            else
                V[i][j] = max(V[i - 1][j], V[i - 1][j - w[i]] + v[i]);
        }
    }
    j = C;
    for (i = n - 1; i >= 0; i--)
    {
        if (V[i][j]>V[i - 1][j])
        {
            x[i] = 1;
            j = j - w[i];
        }
        else
            x[i] = 0;
    }
    printf("選中的物品是:\n");
    for (i = 0; i<n; i++)
        printf("%d ", x[i]);
    printf("\n");
    for (int i = 0; i < n; i++){
        for (int j = 0; j < C+1; j++){
            printf("%d\t ", V[i][j]);
            if (j == C){
                printf("\n");
            }
        }
    }
    return V[n - 1][C];

}

int main()
{
    int s;//獲得的最大價值
    int w[5] = {2,2,6,5,4};//物品的重量
    int v[5] = {6,3,5,4,6};//物品的價值
    int x[5];//物品的選取狀態
    int n = 5;
    int C=10;//背包最大容量

    s = KnapSack(n, w, v, x, C);

    printf("最大物品價值為:\n");
    printf("%d\n", s);
    system("pause");
    return 0;

}

運行結果:

技術分享

動態規劃之01背包問題(含代碼C)