01背包
01背包可以说是比较经典的一个算法了,它是动态规划的精髓。01背包问题一般是一个这样的问题:
有 N N N种物品,每种物品的体积分别为 w i w_i wi,价值分别为 C i C_i Ci。每种物品只能拿一次。 有一个体积为 M M M的背包。请问背包能带走最大的价值是多少?
一般人会想到用贪心来做,但是,贪心往往是错误的。看一下一组数据:
N
=
3
N=3
N=3
M
=
5
M=5
M=5
W
=
{
4
,
3
,
2
}
W=\{4,3,2\}
W={4,3,2}
C
=
{
7
,
5
,
3
}
C=\{7,5,3\}
C={7,5,3}
显然答案是
8
8
8,但是用贪心的话答案就是
7
7
7,所以贪心是错误的。
那么该怎么做呢?
O
(
2
n
)
O\left( 2^n\right)
O(2n)肯定会T飞,那么该肿么办呢?
算法解析
令
f
i
,
j
f_{i,j}
fi,j为当背包体积为
j
j
j的时候,有
i
i
i件物品时所带走最大的价值。显然有两种做法:不取、取。
如果不取,那么值就是
f
i
−
1
,
j
f_{i-1,j}
fi−1,j。
如果取,那么就要在背包内挖出一块
w
i
w_i
wi的空间来存储,还要加上
c
i
c_i
ci。值就是
f
i
−
1
,
j
−
w
i
+
c
i
f_{i-1,j-w_i}+c_i
fi−1,j−wi+ci。当然还有满足
w
i
≤
j
w_i\leq j
wi≤j
综上得
f
i
,
j
=
max
(
f
i
−
1
,
j
,
f
i
−
1
,
j
−
w
i
+
c
i
)
f_{i,j}=\max\left(f_{i-1,j},f_{i-1,j-w_i}+c_i\right)
fi,j=max(fi−1,j,fi−1,j−wi+ci)
复杂度分析
给出一个代码
int f[maxn][maxn];
//读入已省略
for(i=1;i<=m;i++) f[1][i]=0;
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
if(j>=w[i])
f[i][j]=max(f[i-1][j],f[i-1][j-w[i]]+c[i]);
else f[i][j]=f[i-1][j];
显然我们可以发现空间复杂度为 O ( M N ) O \left(MN\right) O(MN)。时间复杂度也是 O ( M N ) O \left(MN\right) O(MN)。
空间优化
有些时候当
M
N
MN
MN比较大的时候,我们需要进行空间优化。
我们发现,要得到
f
i
f_i
fi这一行,只需要得到
f
i
−
1
f_{i-1}
fi−1就可以了,我们就可以开一个数组int f[2][maxn]
就够了。
当然,我们可以直接开一个一维数组,只不过需要从后往前更新,至于原因…我懒得说