一、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;
}