一、01揹包
事例:
有N件物品和一個容量爲V的揹包。第i件物品所佔用容量是v[i],價值是w[i]。求將哪些物品裝入揹包可使總價值最大。
思路:
- 要使裝入揹包內的物品價值最大,應優先裝入佔地空間小且價值大的物品。
- 我們不妨先找出容積從0到V 的各個容量所裝物品的最優解。(就是指 當揹包容量爲1時裝物品的最優解,當揹包容量爲2時裝物品的最優解······一直到容積爲N時裝物品的最優解)。
- 那麼如何求在容積爲 v 時的最優解呢。那麼就是把每種情況都放一下找出最優的。此時要用到的公式是
f[j]=max(f[j],f[j-weight[i]]+value[i]);
下面來講解一下該公式的含義
f[j]
含義是當揹包容量爲 j 時的價值最優解(即怎樣放物品能使揹包總價值最大)。f[j-weight[i]]
含義是在放入第i件物品前揹包價值的最優解。那麼
f[j-weight[i]]+value[i]
的含義就很簡單了,即放入第i件物品時揹包的價值。那
max(f[j],f[j-weight[i]]+value[i])
就是比較原本揹包容量爲 j 時的最大價值與現在放第i件物品時揹包價值,取最大值成爲新的“揹包容量爲j時的最優解”(我們可以評出的看到揹包的容量一直 j,沒有改變)。
代碼:
#include <iostream>
#include <cstring>
using namespace std;
const int N=4; //一共有N件物品。
const int V=5; //揹包容量爲V。
int weight[N]={1,2,2,3}; //各個物品的體積。
int value[N]={25,10,20,30};//各個物品的價值。
int f[V+1]; //f[j]意味容積j時的最優解。
int zeroonepack()
{
int i,j;
memset(f,0,sizeof(f)); //先把f[]初始化爲0。
for(i=0;i<N;i++) //找出容積爲 j 時向背包裏裝物品的最優解
{
for(j=V;j>=weight[i];j--)
{
f[j]=max(f[j],f[j-weight[i]]+value[i]);
cout << j << " " << f[i] << endl;
}
} //求最優解結束
return f[V]; //輸出容積爲V時的最佳解(即揹包裝滿時的最優解)
}
int main()
{
cout << zeroonepack() << endl; //調用函數
return 0;
}
注意:
有的題目要求“恰好裝滿揹包”時的最優解,有的題目則並沒有要求必須把揹包裝滿。
如果是第一種問法,要求恰好裝滿揹包,那麼在初始化時除了f[0]爲0,其它f[1..V ]均設爲−∞,這樣就可以保證最終得到的f[V ]是一種恰好裝滿揹包的最優解。
二、完全揹包
事例:
有N種物品和一個容量爲V 的揹包,每種物品都可裝入無限次。第i種物品所佔容量爲v[i],價值是W[i]。求將哪些物品裝入揹包可使總價值最大。
思路:
- 可將該問題看成01揹包問題。
- 不同點是:
01揹包是一件物品只能用一次,於是我們從體積爲V開始,倒着減。
完全揹包是從第1件物品開始,只要體積不超過揹包容量V就可以一直裝,那麼一件物品就可以用好多次。
代碼:
#include <iostream>
#include <cstring>
using namespace std;
const int N=4;
const int V=5;
int weight[N]={1,2,2,3};
int value[V]={25,10,20,30};
int f[V+1];
int completepace()
{
memset(f,0,sizeof(f));
int i,j;
for(i=0;i<=N;i++)
{
for(j=weight[i];j<=V;j++) //從第i件物品開始放
{
f[j]=max(f[j],f[j-weight[i]]+value[i]);
cout << j << " " << f[j] << endl;
}
return f[V];
}
}
int main()
{
cout << completepace() << endl;
return 0;
}