前面分析过01背包问题。01背包:在M件物品中取出若干件物品放到背包中,每件物品对应的体积v1,v2,v3,....对应的价值为w1,w2,w3,,,,,每件物品最多拿一件。
解决方法是 动态规划
决策是:第i件物品放或者不放
由此得到状态转移方程:
f[i,j] = max{f[i-1,j], f[i-1,j-wi]+Pi(j>=wi)}
f[i,j]表示前i件物品中选择若干件放在所剩空间为j的背包中。wi表示第i件物品的体积。Pi表示第i件物品的价值。
上述方程可优化为一维数组表达式:
n是物品种类数,V是背包容量,c[i]是物品i的体积,w[i]是物品i的价值,f[v]是容量为v的背包在前i件物品中取得的最大价值
for(int i=1;i<=n;++i){
for(int v =V;v>=c[i];v--){
f[v] = max(f[v],f[v-c[i]]+w[i]);
cout <<"i: "<
这里注意次序,第二个for循环for(int v =V;v>=c[i];v--)依次减小的。
最优解: O(VN)
for i=1..N
for j=0..V
f[j]=max{f[j],f[j-c]+w}
上面的伪代码和01背包不同之处只有v的循环次序不同。
为什么01背包要按照v的逆序来循环。正是为了保证每件物品i只选择一次。为了保证在考虑“选入第i件物品”这件策略时,依据的是一个没有已经选入第i件物品的子结果f[v-c]。
然而,完全背包的特点恰是每种物品可选无限件,所以,必须采用v的顺序循环。
完全背包:
有N种物品和一个容量为V的背包,每种物品都有无限件可用。
第i种物品的体积是c,价值是w。求解将哪些物品装入背包可使这些物品的体积总和不超过背包容量,且价值总和最大。这里不同之处是每件物品可无限取,这里就产生了很多可行的优化,比如同体积的有多种物品,则必然可以舍弃价值小的,也可以舍弃体积大于v的。
直接说题意,完全背包定义有N种物品和一个容量为V的背包,每种物品都有无限件可用。第i种物品的体积是c,价值是w。求解将哪些物品装入背包可使这些物品的体积总和不超过背包容量,且价值总和最大。本题要求是背包恰好装满背包时,求出最大价值总和是多少。如果不能恰好装满背包,输出NO
2 1 5 2 2 2 5 2 2 5 1
1
强调一点:这里唯一不同的是背包如果不能完全装满,则输出NO,这里需要一个技巧,就是初始化时f[0]=0,其余的均为-max(初始化一个大的负数),只有这样最大值为正时,只能通过f[0]在相加其他价值得到,如:
#include
#include
#include
#include
using namespace std;
int f[50010], c[2010], w[2010];
int main()
{
int test, m, v, i, j;
scanf("%d", &test);
while (test--)
{
memset(f, -10000000, sizeof(f)); //用来判断背包是否装满
f[0] = 0;
scanf("%d%d", &m, &v);
for (i = 1; i <= m; ++i)
scanf("%d%d", &c[i], &w[i]);
for (i = 1; i <= m; ++i)
for (j = 0; j <= v; ++j)//注意此循环与01背包的用一维数组表示的状态方程的区别,一个循环逆序,一个顺序
if (j >= c[i])
f[j] = f[j]>(f[j - c[i]] + w[i]) ? f[j] : f[j - c[i]] + w[i];//完全背包的状态方程,可画图加深理解
if (f[v]<0)//背包为装满
printf("NO\n");
else
printf("%d\n", f[v]);
}
}