0/1背包问题
1.分析最优解的结构
0/1背包问题可以形式化表述为一个整数规划问题:
m
a
x
∑
i
=
1
n
v
i
x
i
{
∑
i
=
1
n
w
i
x
i
≤
c
x
i
∈
{
0
,
1
}
1
≤
i
≤
n
max\sum_{i=1}^n{v_ix_i} \ \ \ \ \ \begin{cases} \sum_{i=1}^n{w_ix_i}\leq c \\ x_i \in \{0,1\} & 1\leq i \leq n \end{cases}
maxi=1∑nvixi {∑i=1nwixi≤cxi∈{0,1}1≤i≤n
问题的解为一个n元0-1向量,
(
x
1
,
x
2
,
x
3
,
.
.
.
,
x
n
)
,
x
i
∈
{
0
,
1
}
(
1
≤
i
≤
n
)
(x_1,x_2,x_3,...,x_n),\ x_i \in \{0,1\} \ (1 \leq i \leq n)
(x1,x2,x3,...,xn), xi∈{0,1} (1≤i≤n)。0/1背包问题具有最优子结构性质,因此可以使用动态规划方法解决。最优子结构性质证明如下:
首先假设原问题的一个最优解为
(
x
1
,
x
2
,
x
3
,
.
.
.
,
x
n
)
(x_1,x_2,x_3,...,x_n)
(x1,x2,x3,...,xn),考虑0/1背包的一个子问题为:
m
a
x
∑
i
=
1
n
−
1
v
i
x
i
{
∑
i
=
1
n
−
1
w
i
x
i
≤
c
−
w
n
x
n
x
i
∈
{
0
,
1
}
1
≤
i
≤
n
−
1
max\sum_{i=1}^{n-1}{v_ix_i} \ \ \ \ \ \begin{cases}\sum_{i=1}^{n-1}{w_ix_i}\leq c-w_nx_n\\x_i \in \{0,1\} & 1\leq i \leq n-1\end{cases}
maxi=1∑n−1vixi {∑i=1n−1wixi≤c−wnxnxi∈{0,1}1≤i≤n−1
则相应的子问题的最优解应该为
(
x
1
,
x
2
,
x
3
,
.
.
.
,
x
n
−
1
)
(x_1,x_2,x_3,...,x_{n-1})
(x1,x2,x3,...,xn−1)。假设存在一个解
(
x
1
′
,
x
2
′
,
x
3
′
,
.
.
.
,
x
n
−
1
′
)
(x'_1,x'_2,x'_3,...,x'_{n-1})
(x1′,x2′,x3′,...,xn−1′),使
∑
i
=
1
n
−
1
v
i
x
i
′
>
∑
i
=
1
n
−
1
v
i
x
i
\sum_{i=1}^{n-1}{v_ix'_i}>\sum_{i=1}^{n-1}{v_ix_i}
∑i=1n−1vixi′>∑i=1n−1vixi,由于
∑
i
=
1
n
−
1
w
i
x
i
≤
c
−
w
n
x
n
\sum_{i=1}^{n-1}{w_ix_i}\leq c-w_nx_n
∑i=1n−1wixi≤c−wnxn,可得
∑
i
=
1
n
−
1
w
i
x
i
+
w
n
x
n
≤
c
\sum_{i=1}^{n-1}{w_ix_i}+w_nx_n\leq c
∑i=1n−1wixi+wnxn≤c,即存在一个n元0-1向量
(
x
1
′
,
x
2
′
,
x
3
′
,
.
.
.
,
x
n
−
1
′
,
x
n
)
(x'_1,x'_2,x'_3,...,x'_{n-1},x_n)
(x1′,x2′,x3′,...,xn−1′,xn)是原问题的一个可行解。可以得到:
∑
i
=
1
n
−
1
v
i
x
i
′
+
v
i
x
n
>
∑
i
=
1
n
−
1
v
i
x
i
+
v
i
x
n
=
∑
i
=
1
n
v
i
x
i
\sum_{i=1}^{n-1}{v_ix'_i}+v_ix_n>\sum_{i=1}^{n-1}{v_ix_i}+v_ix_n=\sum_{i=1}^n{v_ix_i}
i=1∑n−1vixi′+vixn>i=1∑n−1vixi+vixn=i=1∑nvixi
即这个可行解计算的结果比原问题的最优解要大,这产生了矛盾。因此可以证明0/1背包问题具有最优子结构性质。
2.建立递归关系
设0/1背包问题的子问题为:
m
a
x
∑
k
=
1
i
v
k
x
k
{
∑
k
=
1
i
w
k
x
k
≤
j
x
k
∈
{
0
,
1
}
1
≤
k
≤
i
max\sum_{k=1}^i{v_kx_k} \ \ \ \ \ \begin{cases} \sum_{k=1}^i{w_kx_k}\leq j \\ x_k \in \{0,1\} & 1\leq k \leq i \end{cases}
maxk=1∑ivkxk {∑k=1iwkxk≤jxk∈{0,1}1≤k≤i
最优解用m(i,j)表示,i表示可选物品1-i,j表示容量为j。根据0/1背包的最优子结构性质,可以得到计算m(i,j)的递归式如下:
i
=
1
m
(
i
,
j
)
=
{
0
0
≤
j
<
w
i
v
i
w
i
≤
j
i
>
1
m
(
i
,
j
)
=
{
m
(
i
−
1
,
j
)
0
≤
j
<
w
i
m
a
x
{
m
(
i
−
1
,
j
)
,
m
(
i
−
1
,
j
−
w
i
)
+
v
i
}
w
i
≤
j
i = 1 \ \ \ \ m(i,j)= \begin{cases} 0 & 0\leq j<w_i \\ v_i & w_i \leq j \end{cases} \\ i>1 \ \ \ \ m(i,j)= \begin{cases} m(i-1,j) & 0\leq j < w_i \\ max\{m{(i-1,j), \ m(i-1,j-w_i)+v_i}\} & w_i \leq j \end{cases}
i=1 m(i,j)={0vi0≤j<wiwi≤ji>1 m(i,j)={m(i−1,j)max{m(i−1,j), m(i−1,j−wi)+vi}0≤j<wiwi≤j
3.计算最优值
根据以上构造的递推关系,进行最优值的计算。
int solution(int m[][c+1],int weight[],int value[],int c,int n){
//初始化,当j>=weight[0],m[0][j]=value[0]
memset(dp,0,sizeof(dp));
for(int j=weight[0];j<=c;j++) m[0][j]=value[0];
for(int i=1;i<n;i++){
for(int j=0;j<=c;j++){
if(j<weight[i]) m[i][j]=m[i-1][j];
else m[i][j]=max(m[i-1][j],m[i-1][j-weight[i]]+value[i]);
}
}
return m[n-1][c];
}
主要的计算量为m[i][j]的计算,因此该算法的时间复杂度为 O ( c n ) O(cn) O(cn)。
4.构造最优解
根据m[i][j]中记录的信息判断最优解是从哪个子问题产生的,得到结果的0-1向量:
void traceback(int m[][c+1], int weight[] ,int n, int c, int x[]){
for(int i=1;i<n;i++){
if(m[i][c]==m[i-1][c]) x[i] = 0;
else{
x[i] = c;
c -= weight[i];
}
}
x[0] = m[0][c]?1:0;
}