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。前面已經對動態規劃的基本概念做了詳細的講解,動態規...