1. 程式人生 > >0-1揹包問題的演算法優化

0-1揹包問題的演算法優化

簡單描述

0-1揹包問題描述如下:

有一個容量為V的揹包,和一些物品。這些物品分別有兩個屬性,體積w和價值v,每種物品只有一個。要求用這個揹包裝下價值儘可能多的物品,求該最大價值,揹包可以不被裝滿。因為最優解中,每個物品都有兩種可能的情況,即在揹包中或者不存在(背 包中有0個該物品或者 1個),所以我們把這個問題稱為0-1揹包問題。

0-1揹包問題狀態轉移方程

用dp[i][j]表示前i個物品在總體積不超過j的情況下,放到揹包裡的最大價值。由此可以推出狀態轉移方程:

dp[0][j] = 0;

dp[i][j] = max{dp[i-1][j-v[i]] + w[i],dp[i-1][j]};

上面的式子應該很好理解,當第i物品的體積小於當前剩餘的體積,則說明可以裝入揹包,那麼dp[i][j] = dp[i-1][j-v[i]]+w[i]。反之就是不能轉入揹包,dp[i][j] = dp[i-1][j]。

0-1揹包問題實現演算法1

#include <iostream>
using namespace std;

#define MAXSIZE 100
int w[MAXSIZE];
int v[MAXSIZE];
int maxv;
int n;
int dp[MAXSIZE][MAXSIZE];

int max(int a, int b)
{
    if (a > b)
        return a;
    else
        return b;
}

int main()
{
    cin >> n >> maxv;
    for (int i = 1; i <= n; i++)
    {
        cin >> w[i] >> v[i];
    }
    for (int i = 0; i <= maxv; i++)
        dp[0][i] = 0;

    for (int i = 1; i <= n; i++)
    {
        //只有當j >= w[i],dp[i][j]才能進行選取最大值
        for (int j = maxv; j >= w[i]; j--)
        {
            dp[i][j] = max(dp[i - 1][j], dp[i - 1][j - w[i]] + v[i]);
        }
        
        //當j < w[i],說明第i個物品是不能轉入揹包的,故dp[i][j] = dp[i-1][j]
        for (int j = w[i] - 1; j >= 0; j--)
            dp[i][j] = dp[i - 1][j];
    }

    cout << dp[n][maxv] << endl;
    return 0;
}

 看到這裡,還沒完,哈哈!下面介紹一下對dp的優化,我們發現這裡的dp是一個二維陣列,其實dp完全可以用一維陣列表示。為啥子???

0-1揹包問題實現演算法2

看這裡:可以發現0-1揹包的狀態轉移方程 dp[i][j] = max{dp[i-1][j-w[i]]+v[i],dp[i-1][j]}的特點,當前狀態僅依賴前一狀態的剩餘體積與當前物品體積v[i]的關係。根據這個特點,我們可以將dp降到一維即dp[j] = max{dp[j],dp[j-w[i]]+v[i]}。從這個方程中我們可以發現,有兩個dp[j],但是要區分開。等號左邊的dp[j]是當前i的狀態,右邊中括號內的dp[j]是第i-1狀態下的值。

所以為了保證狀態的正確轉移,我們需要先更新等號左邊中的dp[j](當前狀態的dp[j])。

#include <iostream>
using namespace std;

#define MAXSIZE 100
int w[MAXSIZE];
int v[MAXSIZE];
int maxv;
int n;
int dp[MAXSIZE];

int max(int a, int b)
{
    if (a > b)
        return a;
    else
        return b;
}

int main()
{
    cin >> n >> maxv;
    for (int i = 1; i <= n; i++)
    {
        cin >> w[i] >> v[i];
    }
    for (int i = 0; i <= maxv; i++)
        dp[i] = 0;

    for (int i = 1; i <= n; i++)
    {
        //只有當j >= w[i],dp[j]才能進行選取最大值,否則dp[j]將不作更新,等於dp[i-1][j]。
        for (int j = maxv; j >= w[i]; j--)
        {
            dp[j] = max(dp[j], dp[j - w[i]] + v[i]);
        }
        
    }

    cout << dp[maxv] << endl;
    return 0;
}

比較上面的兩個演算法可以發現,它們的時間複雜度都是O(n*maxv),只有空間複雜度發生變化。後者的空間複雜度得到了優化。

拓展0-1揹包問題

哈哈,還沒完,繼續0-1揹包問題,如果在上面的問題加上一個限制條件,所選擇的物品必須恰好裝滿揹包,否則輸出-1。

同樣的給出兩種演算法,它們的時間複雜度都是一樣的,只不過是空間複雜度不同。

空間複雜度為O(n*maxv)的演算法

#include <iostream>
using namespace std;

#define MAXSIZE 100
int w[MAXSIZE];
int v[MAXSIZE];
int maxv;
int n;
int dp[MAXSIZE][MAXSIZE];

int max(int a, int b)
{
    if (a > b)
        return a;
    else
        return b;
}

int main()
{
    cin >> n >> maxv;
    for (int i = 1; i <= n; i++)
    {
        cin >> w[i] >> v[i];
    }
    //初始化,當容積為0時,即不能裝入,最大價值即為0
    for (int i = 1; i <= n; i++)
    {
        dp[i][0] = 0;
    }

    //初始化為-1,表示沒有裝滿
    for (int i = 0; i <= n; i++)
    for (int j = 1; j <= maxv; j++)
        dp[i][j] = -1;

    for (int i = 1; i <= n; i++)
    {
        for (int j = maxv; j >= w[i]; j--)
        {
            //dp[i - 1][j - w[i]] != -1表示容積為j - w[i]時沒有裝滿,所以當容積為j,裝w[i]時一定不能裝滿
            //dp[i - 1][j - w[i]] + v[i] > dp[i-1][j]表示裝入物品i時簽好裝滿並且總價值比前i-1個物品的總價值要大
            if (dp[i - 1][j - w[i]] != -1 && dp[i - 1][j - w[i]] + v[i] >= dp[i - 1][j])
                dp[i][j] = dp[i - 1][j - w[i]] + v[i];

        }
        for (int j = w[i] - 1; j >= 1; j--)
            dp[i][j] = dp[i - 1][j];
    }

    cout << dp[n][maxv] << endl;

    return 0;
}

空間複雜度為O(maxv)的演算法

#include "stdafx.h"
#include <iostream>
using namespace std;

#define MAXSIZE 100
int w[MAXSIZE];
int v[MAXSIZE];
int maxv;
int n;
int dp[MAXSIZE];

int max(int a, int b)
{
    if (a > b)
        return a;
    else
        return b;
}

int main()
{
    cin >> n >> maxv;
    for (int i = 1; i <= n; i++)
    {
        cin >> w[i] >> v[i];
    }
    //初始化,當容積為0時,即不能裝入,最大價值即為0
    dp[0] = 0;

    //初始化為-1,表示沒有裝滿
    for (int j = 1; j <= maxv; j++)
        dp[j] = -1;

    for (int i = 1; i <= n; i++)
    for (int j = maxv; j >= w[i]; j--)
    {
        if (dp[j - w[i]] != -1 && dp[j - w[i]] + v[i] >= dp[j])
            dp[j] = dp[j - w[i]] + v[i];
    }
    cout << dp[maxv] << endl;

    return 0;
}

從上面的演算法我們發現,這裡的狀態轉移方程和0-1揹包問題的狀態轉移方程是一樣一樣滴,只不過是初試狀態發生了一點改變。

呵呵,到這裡0-1揹包問題先結束了,後面會繼續介紹更加複雜的揹包問題。

ps:走一步,學一步,總結一步,路就不會太遠,目標也不會遙不可及。

相關推薦

0-1揹包演算法(動態規劃)

給定n種物品和一揹包。物品i的重量是wi,其價值為vi,揹包的容量為C。問應如何選擇裝入揹包的物品,使得裝入揹包中物品的總價值最大? 0-1揹包問題是一個特殊的整數規劃問題。 設所給0-1揹包問題的子問題 的最優值為m(i,j),即m(i,j)是揹包容量為j,可選擇物品為

0-1揹包優化動態規劃演算法之跳躍點法

// 動態規劃 揹包問題 跳躍點優化 #include <iostream> using namespace std; template<class Type> void Traceback(int n,Type w[],Type v[]

0-1揹包問題的演算法優化

簡單描述 0-1揹包問題描述如下: 有一個容量為V的揹包,和一些物品。這些物品分別有兩個屬性,體積w和價值v,每種物品只有一個。要求用這個揹包裝下價值儘可能多的物品,求該最大價值,揹包可以不被裝滿。因為最優解中,每個物品都有兩種可能的情況,即在揹包中或者不存在(背 包中有0個該物品或者 1個),所以我們把

演算法 | 0-1揹包

   #include<stdio.h> #include<string.h> #define MaxN 10000 #define MaxC 10000 int Val[MaxN][MaxN]; double binaryKnapsack(int numItem

0-1揹包問題-貪心演算法

    今天用貪心演算法給出揹包問題的一種解,雖然貪心演算法不一定是最優解,但是在資料量極大時,貪心演算法可以快速獲得接近最優解的答案 package test; import java.util.ArrayList; import 

0-1揹包問題—回溯演算法—java實現

0-1揹包問題 【問題描述】 有n種可選物品1,…,n ,放入容量為c的揹包內,使裝入的物品具有最大效益。 表示 n :物品個數 c :揹包容量 p1,p2, …, pn:個體物品效益值 w1,w2, …,wn:個體物品容量 【問題解析】 0-1揹包問題的解指:物品1,…,n的一種放

0-1揹包問題-回溯演算法

    回溯演算法類似於遍歷的求解,但不同於無腦遍歷的的地方是它在每一步都判斷是否滿足約束條件,及回溯點,所以可以理解為有條件的遍歷。使用回溯演算法求解01揹包最優解時需要建立二叉樹,樹有業務意義的深度為物品數量n,加上根節點總深度為n+1,除了終端節點外,每個葉子

演算法】動態規劃解決0-1揹包的兩個疑惑

1. 揹包問題 描述: 給定 n 種物品,每種物品有對應的重量weight和價值value,一個容量為 maxWeight 的揹包,問:應該如何選擇裝入揹包的物品,使得裝入揹包中的物品的總價值最大? 過程:   a) 把揹包問題抽象化(X1,X2,…,Xn,其中 Xi

NWPU演算法複習--0-1揹包問題

0-1揹包問題 描述 需對容量為c 的揹包進行裝載。從n 個物品中選取裝入揹包的物品,每件物品i 的重量為wi ,價值為pi 。對於可行的揹包裝載,揹包中物品的總重量不能超過揹包的容量,最佳裝載是指所裝入的物品價值最高。 輸入 多個測例,每個測例的輸入佔三行。

貪心演算法0-1揹包

“0-1 揹包問題 問題描述 有一容積有限的揹包(容積為V) 現在有n個物品,每個物品都有自己的價值和提及 如何知道一個較優的策略,使得能夠放進揹包裡價值之和最大的的物品  解題的思路 先把各個物品的價值密度求出來

貪心演算法解決0-1揹包問題

揹包問題描述如下:  已知   揹包容量M=120   物品種類數n=10   各種物品的總效益pi(i=1,2,………10) : 50,60,70,80,90,80,70,60,50,40   各種物品的總重量wi(i=1,2………10) : 17,30,25,41,80,

0-1揹包問題:(回溯演算法

#include <iostream> #define N 205 using namespace std; double w[N],v[N]; double CurW=0; double CurV=0; double c; double BestV=0; int BestX[N

9.29-貪心演算法(//活動安排//0-1揹包//裝載問題)

1.活動安排 描述:Jack是一名nwpu的大一新生,對學校舉辦的各種活動都十分的好奇,想盡可能多的參加這些活動。Npwu每天共有N項活動,其開始結束時間分別為B[i],E[i],(i = 1,2,……N) 請問Jack一天最多能參加幾項活動。當然,Jack在同一時間內只能

0-1揹包問題的多種演算法設計與分析

0-1揹包問題的多種演算法設計與分析 0-1揹包問題描述:給定一組共n個物品,每種物品都有自己的重量wi(i=1~n)和價值vi(i=1~n),在限定的總重量(揹包的容量C)內,如何選擇才能使得選擇物品的總價值之和最高。選擇最優的物品子集放置於給定揹包中,最優子集對應n元解

回溯演算法-子集樹-0-1揹包問題

0-1揹包: 即每種物品只有2 種選擇,分別為:裝入揹包或不裝入揹包,物品數和揹包容量已給定,計算裝入揹包物品的最大價值和最優裝入方案,用回溯法搜尋子集樹的演算法進行求解。對此模型我們剛好建立二叉樹(

用模擬退火演算法解決0-1揹包問題

clear clc a = 0.95 k = [5;10;13;4;3;11;13;10;8;16;7;4]; k = -k; % 模擬退火演算法是求解最小值,故取負數 d = [2;5;18;3;2;5;10;4;11;7;14;6]; restriction = 46;

動態規劃演算法0-1揹包問題java實現

問題描述:給定n種物品和一揹包,物品i的重量是wi,其價值是pi,揹包的容量是M,問如何選擇裝入揹包中的物品總價值最大? import java.util.ArrayList; import java.util.HashMap; /* * 實際就是一種分而治之的思想

0-1揹包回溯

限定條件: 如果放入該物品<剩餘揹包容量則回溯。 如果當前價值+剩餘容量下剩餘物品的最大價值<當前最大價值則回溯。(剩下在怎麼放都不會比當前最大價值大,就沒必要算了) 測試: 第一行分別輸入物品數量n與揹包容量c 用例: 10 30095 8975 5923 1973 4350 100

0-1揹包問題(動態規劃)

http://acm.hdu.edu.cn/showproblem.php?pid=1171 這道題咋看有點複雜,其實也只是換了一種思維,因為題目要求要儘量平均分配,所以我們可以先將總價值sum求出,然後得出其分配的平均值為sum/2,要注意這個答案可能為小數,但是又因為sum是整數,所以最

HDU 2639 Bone Collector II(0-1揹包第k優解)

題意: 已知物品的個數、揹包的容量、每個物品的價值和體積,求第k優解; 思路: 和0-1揹包相似,就是陣列加了多一維,不同的是對於第i個物品選和不選的問題,0-1揹包中是直接求的max(dp[j],d[j-w[i]]+v[i]);而在這裡因為要求第k優解,需要將選(mv[])和不