浅谈多重背包

来源:互联网 发布:炼数成金算法导论视频 编辑:程序博客网 时间:2024/06/05 09:08
多重背包,基础背包问题之三。


具体问题是这样的。一个容量为V的背包,还有一些物品(每个物品有具体的数量c),这些物品的体积w和价值p各不相同。求出能在不超过V的情况下尽可能的使价值最大。


对于多重背包问题,可以根据每个物品的数量分解成两种情况:
1.当c >= V / w时,相当于物品可以无限使用,即转化为完全背包求解。
2.当c < V / w时, 可转化为放物品的数量为1、2、3……c个,即转化为01背包求解。


但是上面这种方法的时间复杂度为O(V连加M),其中M为数量。这个复杂度是很大的,可以用二进制拆分优化一下。


二进制拆分:任何数num都可以拆分为2^k的加和的形式,即:
num = 2^k1 + 2^k2 + …… + 2^kn (其中kn可以取任意非负整数)
这样上面的第二种情况就可以利用这种方法进行优化,使遍历从1、2、3……c优化到1、2、4……2^k,时间复杂度为O(V连加logM)。这种优化程度就可以应对大部分题目了。(当然,对于一些卡时间的题目也不行,需要优先队列优化)。
还有一点,看懂这一点就能看懂二进制优化的核心了。
对于使用二进制拆分成的一组数字:1、2、4、8……,这些数字之间缺少3、5、6、7这样的数字,在遍历的时候没有遍历他们,因为这些数字可以由前面遍历过的数字拼凑合成。比如,3可以由1和2合成,5可以由1和4合成,以此类推。还有,比如,如果最终结果包含5,那在遍历的时候就可能记录1和4,也就是说最终结果是1和4这两件被拆分的物品,最后合到一起就是5了。
就是这种方法,使不用被遍历的数字也能出现在过程中,这就是二进制优化的精髓!


以下为程序流程:
void MultiplePack(int w, int p, int m)
{
if (w * m >= p)
{
CompletePack(w, p);
return;
}
int k = 1;
while (k < m)
{
ZeroOnePack(k * w, k * p);
m -= k;
k *= 2;
}
ZeroOnePack(m * w, m * p);

}


下面附上多重背包的模板:

#include <iostream>#include <algorithm>using namespace std;#define MAXL 10001int dp[MAXL], V, n, w, p, m;void ZeroOnePack(int w, int p);void CompletePack(int w, int p);void MultiplePack(int w, int p, int m);int main(){cin >> V >> n;for (int i = 1; i <= n; i++){cin >> w >> p >> m;MultiplePack(w, p, m);}cout << dp[V] << endl;return 0;}void MultiplePack(int w, int p, int m){if (w * m >= p){CompletePack(w, p);return;}int k = 1;while (k < m){ZeroOnePack(k * w, k * p);m -= k;k *= 2;}ZeroOnePack(m * w, m * p);}void ZeroOnePack(int w, int p){for (int j = V; j >= w; j--)dp[j] = max(dp[j - w] + p, dp[j]);}void CompletePack(int w, int p){for (int j = w; j <= V; j++)dp[j] = max(dp[j - w] + p, dp[j]);}


0 0
原创粉丝点击