多重背包的优化 二进制/单调队列解析
2017-10-25 22:32
417 查看
由于做题的时候老被这玩意儿卡住的我很不爽,决定写个blog来加深自己的印象以及不用到处找资料回忆。
多重背包的问题的具体描述如下:
给出一个体积为v的背包,有n个物品,每个物品可以选c[i]次,问最多能得到多大的代价。
直接做DP的复杂度是n*v*max(c[i]),这显然无法承受正常难度的数据范围。
然后我们考虑优化。
首先来看稍微简单一点的二进制优化。
很久以前打的就不要纠结是不是c艹了。
具体的思想就是,如果能选的总和超过体积就做完全背包,否则就对其进行二进制拆分做01背包。然后说说二进制拆分是怎么一回事。
比如一个次数为7=111的物品,他可以被分解001 010 100三种数字。
这三种数字任意组合可以组合成不重复的,小于等于7的数字。
那么我们就是相当于对这三个数做01背包,只不过代价和价值都被累加在一起而已。
简而言之就是相当于把一个能选c[i]次的物品拆成log个只能选一次的,价值体积累加在一起的物品,所以时间复杂度是O(N*V*sigma log(c[i]))
在大部分情况下二进制拆分可以应对绝大多数的多重背包,但是也有一些题目会卡常,比如N,V<=5000就会GG,这个时候就要用到线性但是较难理解的单调队列优化。
具体来说就是:
f[i][j]=(f[i−1][j−k∗w[i]]+k∗v[i])
设m[i]为合法的,i能选的最多次数。
(接下来一段比较复杂,这里直接粘贴了)
代码:
多重背包的问题的具体描述如下:
给出一个体积为v的背包,有n个物品,每个物品可以选c[i]次,问最多能得到多大的代价。
直接做DP的复杂度是n*v*max(c[i]),这显然无法承受正常难度的数据范围。
然后我们考虑优化。
首先来看稍微简单一点的二进制优化。
很久以前打的就不要纠结是不是c艹了。
uses math; const mo=1000000007; var i,j,k,n,m:longint; dp,c,w,num:array[0..100000]of longint; procedure zpack(cost,weight,n:longint); var i:longint; begin for i:=n downto cost do dp[i]:=max(dp[i],dp[i-cost]+weight); end; procedure cpack(cost,weight,n:longint); var i:longint; begin for i:=cost to n do dp[i]:=max(dp[i],dp[i-cost]+weight); end; function multipack(n,m:longint):longint; var i,j,k:longint; begin fillchar(dp,sizeof(dp),0); for i:=1 to n do begin if num[i]*c[i]>m then cpack(c[i],w[i],m) else begin k:=1; while k<num[i] do begin zpack(k*c[i],k*w[i],m); num[i]:=num[i]-k; k:=k*2; end; zpack(num[i]*c[i],num[i]*w[i],m); end; end; exit(dp[m]); end; begin readln(n,m); for i:=1 to n do readln(c[i],w[i],num[i]); writeln(multipack(n,m)); end.
具体的思想就是,如果能选的总和超过体积就做完全背包,否则就对其进行二进制拆分做01背包。然后说说二进制拆分是怎么一回事。
比如一个次数为7=111的物品,他可以被分解001 010 100三种数字。
这三种数字任意组合可以组合成不重复的,小于等于7的数字。
那么我们就是相当于对这三个数做01背包,只不过代价和价值都被累加在一起而已。
简而言之就是相当于把一个能选c[i]次的物品拆成log个只能选一次的,价值体积累加在一起的物品,所以时间复杂度是O(N*V*sigma log(c[i]))
在大部分情况下二进制拆分可以应对绝大多数的多重背包,但是也有一些题目会卡常,比如N,V<=5000就会GG,这个时候就要用到线性但是较难理解的单调队列优化。
具体来说就是:
f[i][j]=(f[i−1][j−k∗w[i]]+k∗v[i])
设m[i]为合法的,i能选的最多次数。
(接下来一段比较复杂,这里直接粘贴了)
若用F[i][j]表示对容量为j的背包,处理完前i种物品后,背包内物品可达到的最大总价值,并记m[i] = min(n[i], j / v[i])。放入背包的第i种物品的数目可以是:0、1、2……,可得: F[i][j] = max { F[i - 1] [j – k * v[i] ] + k * w[i] } (0 <= k <= m[i]) ㈠ 如何在O(1)时间内求出F[i][j]呢? 先看一个例子:取m[i] = 2, v[i] = v, w[i] = w, V > 9 * v, 并假设 f(j) = F[i - 1][j],观察公式右边要求最大值的几项: j = 6*v: f(6*v)、f(5*v)+w、f(4*v)+2*w 这三个中的最大值 j = 5*v: f(5*v)、f(4*v)+w、f(3*v)+2*w 这三个中的最大值 j = 4*v: f(4*v)、f(3*v)+w、f(2*v)+2*w 这三个中的最大值 显然,公式㈠右边求最大值的几项随j值改变而改变,但如果将j = 6*v时,每项减去6*w,j=5*v时,每项减去5*w,j=4*v时,每项减去4*w,就得到: j = 6*v: f(6*v)-6*w、f(5*v)-5*w、f(4*v)-4*w 这三个中的最大值 j = 5*v: f(5*v)-5*w、f(4*v)-4*w、f(3*v)-3*w 这三个中的最大值 j = 4*v: f(4*v)-4*w、f(3*v)-3*w、f(2*v)-2*w 这三个中的最大值 很明显,要求最大值的那些项,有很多重复。 根据这个思路,可以对原来的公式进行如下调整: 假设d = v[i],a = j / d,b = j % d,即 j = a * d + b,代入公式㈠,并用k替换a - k得: F[i][j] = max { F[i - 1] [b + k * d] - k * w[i] } + a * w[i] (a – m[i] <= k <= a) ㈡ 对F[i - 1][y] (y= b b+d b+2d b+3d b+4d b+5d b+6d … j) F[i][j]就是求j的前面m[i] + 1个数对应的F[i - 1] [b + k * d] - k * w[i]的最大值,加上a * w[i],如果将F[i][j]前面所有的F[i - 1][b + k * d] – k * w放入到一个队列,那么,F[i][j]就是求这个队列最大长度为m[i] + 1时,队列中元素的最大值,加上a * w[i]。因而原问题可以转化为:O(1)时间内求一个队列的最大值。
代码:
#include<iostream> #include<cstdio> #include<cstdlib> #include<cstring> #include<algorithm> #define N 205 #define M 20005 #define inf 0x3f3f3f3f using namespace std; int b ,c ,f[M],q[M],w[M]; int main() { int n,m; scanf("%d",&n); for (int i=1;i<=n;i++) scanf("%d",&b[i]); for (int i=1;i<=n;i++) scanf("%d",&c[i]); scanf("%d",&m); memset(f,inf,sizeof(f)); f[0]=0; for (int i=1;i<=n;i++) { for (int j=0;j<b[i];j++) { int head=1,tail=0; for (int k=j;k<=m;k+=b[i]) { while (head<=tail&&w[head]<k-c[i]*b[i]) head++; while (head<=tail&&f[k]-(k-w[head])/b[i]<=q[tail]) tail--; q[++tail]=f[k]; w[tail]=k; f[k]=min(f[k],q[head]+(k-w[head])/b[i]); } } } printf("%d",f[m]); return 0; }
相关文章推荐
- 算法解析之感想---单调队列优化多重背包思路
- poj 2754 Similarity of necklaces 2 转换成多重背包,单调队列优化/ 二进制优化
- POJ - 1742 Coins 多重背包+(二进制优化||单调队列优化)
- 【多重背包】二进制优化 && 单调队列优化 && w == v 的特殊情况的处理
- poj 1276 多重背包+二进制优化+单调队列优化
- HDOJ 2191 (多重背包/二进制分解/单调队列优化DP)
- 多重背包模板--二进制优化模板&&单调队列优化模板
- 背包模板(01,完全,多重背包的二进制优化和单调队列优化
- 多重背包,二进制优化,单调队列优化
- hdu 2191(单调队列优化多重背包模板)
- 多重背包(单调队列优化)
- hdu2191(单调队列优化dp,多重背包)
- 单调队列优化多重背包
- poj1742coins【单调队列优化多重背包】楼教主八题
- 单调队列优化多重背包(含构造问题<POJ 1742 coin>)
- poj1742 单调队列优化多重背包
- [Bzoj4182]Shopping(点分治)(树上背包)(单调队列优化多重背包)
- 单调队列优化的多重背包
- POJ 1742 Coins( 单调队列优化多重背包)
- 多重背包单调队列优化