多重背包问题描述:
有 N N N种物品,第 i i i种物品的体积是 c i c_i ci,价值是 w i w_i wi,每种物品的数量都是有限的,为 n i n_i ni。现有容量为 V V V的背包,求在总体积不超过 V V V的条件下,使得背包的总价值最大。
朴素算法:
将第
i
i
i类物品的
n
i
n_i
ni个物品拆分,得
Σ
n
i
\Sigma{n_i}
Σni个物品,即将原问题转换为了01背包问题,时间复杂度为
O
(
V
×
Σ
n
)
O(V\times\Sigma{n})
O(V×Σn)。
也可以在转移的过程中枚举
k
k
k,表示第
i
i
i种物品选取的数量。
d
p
[
i
]
[
v
]
=
m
a
x
(
d
p
[
i
−
1
]
[
v
−
k
∗
c
i
]
+
k
∗
w
i
)
,
0
≤
k
≤
n
i
dp[i][v]=max(dp[i-1][v-k*c_i]+k*w_i),0\leq{k}\leq{n_i}
dp[i][v]=max(dp[i−1][v−k∗ci]+k∗wi),0≤k≤ni
时间复杂度为
O
(
V
×
Σ
n
)
O(V\times\Sigma{n})
O(V×Σn)。
优化(二进制拆分):
二进制拆分:
一个数
n
n
n可以拆分为
x
x
x个数字,分别为
2
0
,
2
1
,
2
2
,
.
.
.
,
2
k
−
1
,
n
−
2
k
+
1
,
其中
k
是满足
n
−
2
k
+
1
>
0
的
最
大
整
数
。
2^0,2^1,2^2,...,2^{k-1},n-2^{k}+1,\text{其中}k\text{是满足}n-2^k+1>0的最大整数。
20,21,22,...,2k−1,n−2k+1,其中k是满足n−2k+1>0的最大整数。
满足使得这
x
x
x个数可以组合成任意小于等于
n
n
n的数。
由 n − 2 k + 1 > 0 n-2^k+1>0 n−2k+1>0,移项,两边同时取数,得 k < l o g ( n + 1 ) k<log(n+1) k<log(n+1),即拆分得的数字个数 x x x = ⌊ l o g ( n + 1 ) ⌋ =\lfloor{log(n+1)}\rfloor =⌊log(n+1)⌋。
e . g . e.g. e.g. 7的二进制 7 = 111 分解所得的 001 , 010 , 100 001,010,100 001,010,100这三个数可以组合成任意小于等于7 的数,每种组合都会得到不同的数。15 = 1111 可分解成 0001 , 0010 , 0100 , 1000 0001,0010,0100,1000 0001,0010,0100,1000四个数字,这四个数字进行组合也可以得到1-15之间的任一个数值。
优化:
将第
i
i
i种物品的
n
i
n_i
ni个物品进行二进制拆分,得到得到拆分后的
x
x
x个物品,分别为
(
c
i
,
w
i
)
,
(
2
×
c
i
,
2
×
w
i
)
,
(
4
×
c
i
,
4
×
w
i
)
,
.
.
.
,
(
2
x
−
1
×
c
i
,
2
x
−
1
×
w
i
)
,
(
(
n
−
2
x
+
1
)
×
c
i
,
(
n
−
2
x
+
1
)
×
w
i
)
(c_i,w_i),(2\times c_i,2\times w_i),(4\times c_i,4\times w_i),...,(2^{x-1}\times c_i,2^{x-1}\times w_i),((n-2^x+1)\times c_i,(n-2^x+1)\times w_i)
(ci,wi),(2×ci,2×wi),(4×ci,4×wi),...,(2x−1×ci,2x−1×wi),((n−2x+1)×ci,(n−2x+1)×wi)。
e
.
g
.
e.g.
e.g.当
n
n
n为13时,
x
=
⌊
l
o
g
(
13
+
1
)
⌋
=
3
x=\lfloor{log(13+1)}\rfloor=3
x=⌊log(13+1)⌋=3,故拆分成
1
,
2
,
4
,
6
1,2,4,6
1,2,4,6,根据二进制的性质,
1
∼
13
1\sim13
1∼13都可以由
1
,
2
,
4
,
6
1,2,4,6
1,2,4,6这四个数字组合得到。
例题:平分娃娃
#include<bits/stdc++.h>
using namespace std;
int m[7];
int w[14*6+1];
int dp[420001];
int main()
{
int V=0;
int t=1;
int j;
memset(dp,0,sizeof(dp));
for(int i=1;i<=6;i++){
cin>>m[i];
V+=m[i]*i;
int k=log2(m[i]+1);
for(j=0;j<=k-1;j++){
w[t++]=i*pow(2,j);
}
w[t++]=i*(m[i]-pow(2,k)+1);
}
if(V%2==0){
for(int i=1;i<=t-1;i++){
for(int j=V;j>=w[i];j--){
dp[j]=max(dp[j],dp[j-w[i]]+w[i]);
}
}
if(dp[V/2]==V/2){
cout<<"Can be divided.\n";
}
else{
cout<<"Can't be divided.\n";
}
}
else{
cout<<"Can't be divided.\n";
}
return 0;
}