入门DP总结(上)
1.简单背包问题
(1) 01背包
·简单题意:N件物品,每件有对应价值和占用容量,背包容量为V,每种物品使用一次。求不超过容量装入物品时的价值最大的方案。
·思路(DP):
1.找出一种状态——>f[i][j] :表示仅从前i个物品选,容量为j且价值最大的选法
2.找出与他有联系的状态(一般为前面的状态) f[i][j]针对第i件物品可分为两种情况:不选择第i件物品/选择第i件物品
3.开始基本操作:为了表示两种情况,需要上一个状态与本次选法的叠加:
f[i][j] = max(f[i-1][j],f[i-1][j-v[i]]+w[i])
//上一种状态则是只考虑前i-1个物品,并且容量为j。
而如果要选择第i件物品,那么上一种状态则是考虑前i-1个物品,容量为
j-v[i]。
·优化方法:
利用滚动数组,可以优化01背包的空间复杂度:
for(int i = 0; i < n ; i ++ )
for(int j = m; j > v[i]; j -- )
{
f[j] = max(f[j],f[j-v[i]]+w[i]);
}
//为什么要倒序:若不倒序,则会用这一次的状态来滚动更新,是错误的。
//我们需要的是i-1层的状态
(2) 完全背包
·简单题意:N件物品,每件有对应价值和占用容量,背包容量为V,每种物品可无限使用。求不超过容量装入物品时的价值最大的方案。
·思路(DP):
该题只需要对01背包的划分进行修改即可,上一次分为了两种情况,实际上是因为每个物品只能选一次,所以情况则是:选0件(不选)和选1件。本题可以选无限件,所以就一直分到装不下为止即可
for(int k = 0; k*v[i] <= j; k ++ )
{
f[i][j] = max(f[i][j],f[i-1][j-k*v[i] + k*w[i]);
}
·优化方法:
利用错位相减:f[i][j]与f[i][j-v[i]]推出:
f[i][j] = max(f[i-1][j],f[i][j-v[i]] + w[i];
再次利用一维优化:
f[j] = max(f[j],f[j-v[i]] + w[i]);
//本次不需要倒序,因为我们需要的是第i层的结果
(3) 多重背包
·简单题意:N件物品,每件有对应价值和占用容量,背包容量为V,每种物品限定使用次数。求不超过容量装入物品时的价值最大的方案。
·思路(DP):
同完全背包,多加约束条件即可O(n³)
·优化方法:<二进制优化>
任何一个数S,都可以以二进制拆分:
S----->log S组拆分
S = 2^0 + 2^1 + 2^2 + 2^3 +...+ 2K,如果有剩余没拆分完,则额外项计为C(C<2(k+1))
因此,利用二进制拆分可以凑出0~S间的所有数
我们在输入时就将所有物品根据其个数拆分为若干个子物品,构建一个新的物品数组,再运用01背包即可优化
(4) 分组背包
·简单题意:N组物品,每组物品有若干个物品,每个有对应价值和占用容量,同一组物品只能选择一个,背包容量为V,每种物品限定使用次数。求不超过容量装入物品时的价值最大的方案。
·思路(DP):
1.找出一种状态->f[i][j], 表示只从前i种物品选,容量为j且价值最大的选法。
2.划分该种状态:f[i][j]在第i组中,有多个物品可以选择,也可以不选第i种物品,同01背包划分。
f[i-1][j-v[i][k]] + w[i][k];
2.线性DP
(1) 数字三角形
7
3 8
8 1 0
2 7 4 4
4 5 2 6 5
·简单题意:从顶部出发,在每一结点可以选择移动至其左下方的结点或移动至其右下方的结点,一直走到底层,要求找出一条路径,使路径上的数字的和最大。
·思路(DP):
1.找出(确定)一种状态->f[i][j],表示从起点到(i,j)的数字总和最大的一条路径
2.划分该种状态:f[i][j]是由上一步走来的,上一步可以是从左上方的点或者是右上方的点,因此:
f[i][j] = max(f[i-1][j-1] + a[i][j],f[i-1][j] + a[i][j]);
(2) 最长上升子序列(LIS)
·简单题意:给定一个长度为 N 的数列,求数值严格单调递增的子序列的长度最长是多少。
·子序列:从前往后 随意选取 的数组成的序列
·思路:
1.确定一种状态:f[i],表示以i为结尾的最长子序列
2.划分该种状态:对i之前的每一个数进行比对,如果小于i,则假设为f[i]序列中的前一个数f[j],进行更新:
f[i] = max(f[i],f[j] + 1);
//其中+1则是加上i后的序列长度
·优化方法:
(3) 最长公共子序列(LCS)
·简单题意:给定两个长度分别为 N 和 M 的字符串 A 和 B,求既是 A 的子序列又是 B 的子序列的字符串长度最长是多少。
·思路:
1.确定一种状态:f[i][j],表示第1个序列的前i个字母与第2个序列的前j个字母构成的公共子序列
2.划分该种状态:
