[DP] 背包问题大总结

@kaike

1.01背包

有N件物品和一个容量为C的背包。第i件物品的重量是w[i],价值是v[i]。求解将哪些物品装入背包可使价值总和最大。

每种物品仅有一件,可以选择放还是不放。

用f[i][c]表示前i件物品恰好放入容量为c的背包里面,总价值最大。

f[i][c]=max{f[i-1][c],f[i-1][c-w[i]]+v[i]}

若不放第i件,就是前i-1件物品放入容量为c的背包里 f[i-1][c]

若放第i件,把前i-1件物品放入容量为 c-w[i] 的背包里,f[i-1][c-w[i]],总价值加上v[i]。

f[i][c]是由 f[i-1][c] 和 f[i-1][c-w[i]] 推出来的

如何在推f[i][c]的时候已知f[i-1][c] 和 f[i-1][c-w[i]]呢

f[i-1][c]就是不要这件物品,直接去取一个值

f[i-1][c-w[i]]就是要这件物品,不过你要取空出这个物品重量的最大价值量的值

由于刚开始时不能出现负值,第一次的时候c不能直接从0开始,需要一个缓冲。

于是二循环就要从背包容量开始倒推

注意看

f[i][c]的值只与f[i-1][c]和f[i-1][c-w[i]]有关,

如此倒推,完全可以转化为 f[c]=max{f[c],f[c-w[i]]+v[i]}

外面的f[c]是新值,里面的f[c]是上次循坏的值也就是f[i-1][c]

f[c-w[i]]同理也是上次循环的值

这样数组从二维到了一维,可以省空间。

时间复杂度(n*c)空间复杂度(c)

最大值当然要从背包满的时候f[c]处取得辣

 

1 for(int i=1;i<=n;i++)
2     for(int j=c;j>0;j--)
3         f[j]=max(f[j],f[j-w[i]]+v[i]);

 

细节问题

1.要求恰好装满背包,f[0]=0,f[1-v]=-∞

2.没要求恰好装满,f[0-v]=0

若要求恰好装满,则只要容量为0的背包恰好装满,而其他的都是未定义的状态

若不要求,所有值都有个确定值

 

2.完全背包问题

有N种物品和一个容量为V的背包,每种物品都有无限件可用。第i种物品的费用是c[i],价值是w[i]。求解将哪些物品装入背包可使这些物品的费用总和不超过背包容量,且价值总和最大。

与01背包不同的是每件可以取无限件

问题十分棘手啊

f[i][c]=max{f[i-1][v-k*w[i]]+k*v[i]} 0<=k*w[i]<=v

若两件物品i、j满足c[i]<=c[j]且w[i]>=w[j],则将物品j去掉,不用考虑。

看起来小优化并没有优化掉什么复杂的东西,毕竟遇到一些极值还是要奔溃的

可以从01背包问题获得点启示

01背包二循环是从c-0开始的,因为他要先预留出来f[c-w[i]]的值

而完全背包就完全不用考虑这个问题,

我们从0-c开始循环

在考虑是否加这个物品的时候,可能正需要一个正好已选入第i个物品的值,所以就正着循环

 

1 for(int i=1;i<=n;i++)
2     for(int j=0;j<=c;j++)
3         f[j]=max(f[j],f[j-w[i]]+v[i]);

 

 

 

 

posted @ 2016-10-11 17:44  kaike  阅读(301)  评论(0编辑  收藏  举报