01揹包問題 (動態規劃演算法)

2021-08-17 10:37:12 字數 4599 閱讀 9587

0-1 揹包問題:給定 n 種物品和乙個容量為 c 的揹包,物品 i 的重量是 wi,其價值為 vi 。

問:應該如何選擇裝入揹包的物品,使得裝入揹包中的物品的總價值最大?

分析一波:面對每個物品,我們只有選擇拿取或者不拿兩種選擇,不能選擇裝入某物品的一部分,也不能裝入同一物品多次。

解決辦法:宣告乙個 大小為  m[n][c](表示當前總價值)的二維陣列,m[ i ][ j ] 表示 在面對第 i 件物品,且揹包容量為 j(沒裝任何物品時的容量,假設揹包就那麼大,只有容量 j ) 時所能獲得的最大價值

,那麼我們可以很容易分析得出 m[i][j] 的計算方法,

(1)j < w[i] 的情況,這時候揹包容量不足以放下第 i 件物品,只能選擇不拿

m[ i ][ j ] = m[ i-1 ][ j ](不拿物品時,價值等於考慮第 i-1 件時的**)

(2)j>=w[i] 的情況,這時揹包容量可以放下第 i 件物品,我們就要考慮拿這件物品是否能獲取更大的價值

如果拿取,m[ i ][ j ]=m[ i-1 ][j-w[ i ] ]+ v[ i ]。 這裡的m[ i-1 ][ j-w[ i ] ]指的就是考慮了i-1件物品,揹包容量為j-w[i]時的最大價值,也是相當於為第i件物品騰出了w[i]的空間。

如果不拿,m[ i ][ j ] = m[ i-1 ][ j ] , 同(1)

究竟是拿還是不拿,自然是比較這兩種情況那種價值最大。

由此可以得到狀態轉移方程

if(j>=w[i])

m[i][j]=max( m[i-1][j], m[i-1][j-w[i]]+v[i] );

else

m[i][j]=m[i-1][j];

例:0-1揹包問題。在使用動態規劃演算法求解0-1揹包問題時,使用二維陣列m[i][j]儲存,m[i][j]陣列值表示價值,揹包容量為j,可選物品為i、i+1、……、n時0-1揹包問題的最優值。繪製

價值陣列v = ,

重量陣列w = ,

揹包容量c = 12時對應的m[i][j]陣列。

0123456789101112

1000

8888

8888

82000

8810

1010

1018

1818

3066

881414

1616

1818

244066

991414

1717

1919

245066

991414

1717

1921

246268

9111416

1719

1921

24(第一行和第一列為序號,其數值為0)

如m[2][6],在面對第2件物品,揹包容量為6時,

我們可以選擇不拿,那麼獲得價值僅為第一件物品的價值8。

即:m[ 2 ][ 6 ] = m[1 ][ 6 ] (m[ i ][ j ] = m[ i-1 ][ j ])

如果拿,就要把第一件物品拿出來,放第二件物品,價值10,那我們當然是選擇拿。

即:m[2][6] = m[2-1][6-6]+10 =0+10 = 10

m[ i ][ j ]=

m[ i-1 ][j-w[ i ]]+ v[ i ]

)依次類推,得到m[6][12]就是考慮所有物品,揹包容量為c時的最大價值。

#include using namespace std;

const int n=15;

int main() ;//注意這裡多了乙個前導0

int w[n]=;

int m[n][n];

int n=6,c=12;

memset(m,0,sizeof(m));

for(int i=1;i<=n;i++)

}for(int i=1;i<=n;i++)   

}  x[1]=(m[1][c]>0)?1:0;  //這裡處理i=1

}  例:

某工廠預計明年有a、b、c、d四個新建專案,每個專案的投資額wk及其投資後的收益vk如下表所示,投資總額為30萬元,如何選擇專案才能使總收益最大?

project專案

wk投資額

vk收益ab

cd結合前面兩段**

#include 

#include 

using

namespace

std;  

const

intn=150;  

intv[n]=;  

intw[n]=;  

intx[n];  

intm[n][n];  

intc=30;  

intn=4;  

void

traceback()  

}  x[1]=(m[1][c]>0)?1:0;  

}  int

main()    }

/*for(int i=1;i<=6;i++)

cout<}*/

traceback();  

for(

inti=1;i<=n;i++)  

cout0;  

}  輸出x[i]陣列:0111,輸出m[4][30]:22。

得出結論:選擇bcd三個專案總收益最大,為22萬元。

不過這種演算法只能得到一種最優解,並不能得出所有的最優解。

其他人解法:空間優化

#include

using

namespace

std;  

intdp[1005][1005];  

intweight[1005];  

intvalue[1005];  

intmain()  

}  cout0;  

}  然後啊,我們來仔細分析分析就會發現,這個陣列開銷還是很大的,因為是二維的,萬一哪個資料一大,分分鐘記憶體超限,因此有了下邊的解法

傳說中的---------------滾動陣列!!!

啊?什麼是滾動陣列。

說白了二維陣列只是把每個物品都跑一遍,然後到最後乙個物品的時候輸出答案,那麼過程值只是計算的時候用一次,我沒必要存下來。所以用乙個陣列去滾動儲存,然後用後乙個狀態的值去覆蓋前面乙個狀態。然後形象的叫它:滾動陣列(ma!dan!一點都不形象,我理解了好久)

好吧,假裝很形象。

那麼問題來了,怎麼樣用一維的去代替二維的工作,或者說怎麼去思考。這是乙個難點。

那麼我們想,遍歷物品的那個for肯定不能省去,然後裡邊的for也不能省。。。。那麼。就把那個i給他刪了吧,好像確實沒啥用哦。

然後就出現了這樣的**

[cpp]view plain

copy

for(

inti=1; i<=n; i++)  

}  看上去好像很厲害的樣子,但是這個絕對是錯的,因為第二個for裡存在某個dp[j]被改動過,然後再次影響到更大的j。就像我們再對乙個陣列進行移位操作,一不小心就全部成了一樣的數。(別笑,你們以前肯定碰到過|||- -)

額。。回到正題,上邊的**會有重複影響,確實歪打正著的碰上了另乙個揹包。這個另說,現在附上正確的思路。

[cpp]view plain

copy

#include

using

namespace

std;  

intdp[1005];

//滾動陣列的寫法,省下空間省不去時間

intweight[1005];  

intvalue[1005];  

intmain()  

}  cout0;  

}  其實就是規定從m開始迴圈,保證了選擇這個物品時,肯定不會重複使用狀態。

01揹包問題 (動態規劃演算法)

題目 給定n種物品和乙個容量為v的揹包,物品i的體積是wi,其價值為ci。每種物品只有乙個 問 如何選擇裝入揹包的物品,使得裝入揹包中的物品的總價值最大?面對每個物品,我們只有選擇放入或者不放入兩種選擇,每種物品只能放入一次。我們用之前同樣的思路來走一遍試試 假設只剩下最後一件物品,我們有兩種選擇 ...

動態規劃演算法 01揹包問題

動態規劃演算法通常用於求解具有某種最優性質的問題。在這類問題中,可能會有許多可行解。每乙個解都對應於乙個值,我們希望找到具有最優值的解。動態規劃演算法與分治法類似,其基本思想也是將待求解問題分解成若干個子問題,先求解子問題,然後從這些子問題的解得到原問題的解。與分治法不同的是,適合於用動態規劃求解的...

動態規劃演算法之0 1揹包問題

我們首先來看一下問題 乙個旅行者有乙個容量為c的揹包,現在有n種物品,每件的重量分別是w1 w 2 w n,每件物品的價值分別為v1 v 2 v n,需要將物品放入揹包中,要怎麼樣放才能保證揹包中物品的總價值最大?具體資料如下表,其中n 4,c 8。前面已經對動態規劃的基本概念做了詳細的講解,動態規...