动态规划
0. 导引
一个问题能用动态规划解决,必须具有以下两个基本条件:
- 最优子结构
- 重叠子问题
算法导论中提到,动态规划有两种等价的实现方法。第一种是自底向上的递推迭代,第二种是带备忘录的自顶向下(带备忘录的递归,如果某个阶段已经计算过,那么直接结束递归)。
还有一个比较重要的是要求子问题间无后效性。有些问题可能不满足这个,或者证明比较麻烦。
下面看个例子。
根据这个递归公式,
- 自底向上:
1 | int W(int a, int b){ |
- 带备忘录的自顶向下:
1 | int W(int a, int b){ |
更多练习,http://acm.hdu.edu.cn/showproblem.php?pid=1579
1. 最大子段和
状态转移方程:
$\tt dp[i]$表示到以第 $\tt i$ 个元素结尾的最大子连续段和。
leetcode 53号问题。
1 | class Solution { |
进阶1:现在给定一个正整数、负整数和 0 组成的 N × M 矩阵,编写代码找出元素总和最大的子矩阵,输出最大的元素总和。题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=1081(杭电这个数据有点水....)
1 |
|
变形:题目同上,不同的是最后输出最大子矩阵的左上角的行号和列号以及右下角的行号和列号。题目链接:https://leetcode-cn.com/problems/max-submatrix-lcci/
1 | class Solution { |
2. 最长上升子序列
状态转移方程:
$\tt dp[i]$表示以第 $\tt i$ 个元素结尾的序列的最长上升子序列。
$\tt Leetcode$ 第300号题
1 | class Solution { |
进阶:编写算法,求出一个数字序列中最长递增子序列的个数。这是Leetcode 673号问题。
1 | class Solution { |
3. 最长公共子序列(lcs)
状态转移方程:
提高:现在不仅要求出最长公共子序列的长度,还要求输出最长公共子序列,该怎么办?
题目链接:http://www.51nod.com/Challenge/Problem.html#problemId=1006
1 |
|
我们需要一个数组记录每次的最大值是从哪个状态转移过来的,要输出的时候,使用递归函数,自顶向下递归,从里到外打印输出。
进阶:现在还要求出两个字符串最长公共子序列的个数有多少?https://www.luogu.com.cn/problem/P2516
1 |
|
4. 最长公共子串(lc)
状态转移方程:
$\tt dp[i][j]$表示str1前i个字符与str2前j个字符的最长公共子串。
5. 背包问题
参考文献:背包问题九讲,崔添翼。
5.1 0-1背包
问题描述:给定容量为 $V$ 的背包,有 $n$ 个物品,每个物品有价值 $w$、体积 $c$ 两个属性,求背包能装的物品最大价值。
求解:二维数组 $dp$,$dp[i][j]$ 表示有 $i$ 个物品,背包容量为 $j$ 的情况下,此时的背包最大价值;$dp[n][V]$即为答案
状态转移公式:
理解状态转移公式:
对于每个背包都有两种选择:装与不装(装的前提是当前容量 $j$ 能装下 $c[i]$),不装的话就是前 $i-1$ 个物品容量为 $j$ 时的价值 $dp[i][j]$;若选择装,则背包需要提前给当前物品留下 $c[i]$ 的空间。
代码实现(杭电2602号问题):
1 |
|
空间压缩:
1 |
|
初始化的细节问题
当有些问题要求“恰好装满背包”时的最优解,这时在初始化的时候,dp数组除了dp[0]为0,其余dp[1…V]均设置为无穷大。可以这样理解:初始化的dp数组事实上就是在没有任何物品可以放入背包时的合法状态。如果要求背包恰好装满,那么此时只有容量为$0$的背包可以在什么也不装且价值为 $0$ 的情况下被“恰好装满”,其它容量的背包均没有合法的解,属于未定义的状态,应该被赋值为无穷大了。如果背包并非必须被装满,那么任何容量的背包都有一个合法解“什么都不装”,这个解的价值为0,所以初始时状态的值也就全部为0了。
还有一种情况就是求背包的最小价值,把转移方程中的$max$改为$min$即可。
- 有些题目中,只给了价值或者重量这一个数组,那么此时我们可以认为数值上价值与重量时一致的。比如只给了价值 $v$,此时01背包状态转移公式就成了:$dp[j+1] = \max(dp[j],dp[j - v[i]] + v[i]),j \in [V,v[i]]] $。
杭电1114号问题就是以上两种情况的结合。题目大意就是给定存钱罐初始重量和装满时候的重量,然后给定几种不同价值和重量的硬币,问能够恰好装满存钱罐的最小价值是多少?每个种类的硬币可以无限使用,这是一个完全背包问题。
1 |
|
- Leetcode 416号问题:划分等和子集。
给定一个只包含正整数的非空数组。是否可以将这个数组分割成两个子集,使得两个子集的元素和相等。
注意:
- 每个数组中的元素不会超过 100
- 数组的大小不会超过 200
事实上这是个$NP$难问题,但在给定的数据规模下,可以转化为0-1背包问题求解。具体就是:给定n件物品,背包容量为 sum / 2 能否选择一些物品,恰好装满背包?
1 | class Solution { |
5.2 完全背包
定义
有 $N$ 种物品和一个容量为 $V$ 的背包,每种物品都有无限件可用。放入第 $i$ 种物品的费用是 $c_i$,价值是 $w_i$。求解:将哪些物品装入背包,可使这些物品的耗费的费用总和不超过背包容量,且价值总和最大。
求解
虽然物品个数是无限的,但是实际上,由于背包容量有上限,每个物品最多选取的个数也是有限制的,这样可以转换成多重背包问题,进而可以转换成 01 背包问题。
1 | for(int i = 1; i <= N; i++){ |
观察状态转移公式,我们可以用$dp[i][j - c_i]$去更新$dp[i][j]$而不用去枚举$k$了,因此状态转移公式就变成:
1 | for(int i = 1; i <= N; i++){ |
空间压缩
1 |
|
可以看到完全背包就是在第二重循环遍历次序与01背包不同而已,01背包是逆序,完全背包是顺序。
5.3 多重背包
定义
有 $N$ 种物品和一个容量为 $V$ 的背包。第 $i$ 种物品最多有 $n_i$ 件可用,每件耗费的空间是 $c_i$,价值是 $w_i$。求解将哪些物品装入背包可使这些物品的耗费的空间总和不超过背包容量,且价值总和最大。
求解
1 |
|
空间优化
1 |
|
多重背包的二进制优化
二进制思想即任意数字都可以由$2$的次幂的数字组合而来(用01表示),这是计算机的基础。利用这个思想,我们可以把每个物品数量分成$1,2,4,…,n - 2^K + 1$,每一组的体积和价值分别为$(c_i,w_i),(2c_i,2w_i),(4c_i,4w_i)$,等等,通过这些组合(选择装与不装)一定可以组成$n$,这样就遍历了$1-n$内所有数字。通过拆分所有物品,就形成了一个新的价值和体积物品组,这些问题组我们可以选择装也可以选择不装,这样利用二进制优化就可以将多重背包转化为01背包问题。
代码实现:
1 | // 二进制优化 |
杭电1059、2844号题是完全背包的二进制优化。
5.4 有约束的背包问题
杭电3466号问题。题目大意是说,给定背包容量,每个物品的重量$P$,若要装入该物品要求的最小背包容量$Q$以及该物品的价值$V$,输出不超过背包容量下所能获取的最大物品价值。
相比于裸01背包,这个题目要求装入的时候必须考虑$P,Q$这两个条件,要用到贪心。即$A$物品$p_1=3,q_1=5$,$B$物品$p_2=4,q_2=3$;背包容量为$7$,若先装$A$,所需容量至少为$p_1 + q_2 = 7$,若先装$B$,所需容量至少为$p_2 + q_1 = 9$,因此应该选择容量小的。即:$p_1 + q_2 < p_2 + q_1 \rightarrow q_1-p_1 > q_2 - p_2$,即$q-p$大的先买。但是转移方程中,更新过程是逆向的,因此排序的时候应该按照$p-q$从小到大排序。
1 |
|
背包练习(以下题目皆来自杭电 on-line judge: http://acm.hdu.edu.cn/ ,做完还不会背包来找我。
2602 1114 1171 2844 1059 2955 1203 3466
6. 编辑距离
问题描述:
给你两个单词word1和word2,请你计算出将 word1 转换成 word2 所使用的最少操作数 。
你可以对一个单词进行如下三种操作:
- 插入一个字符
- 删除一个字符
- 替换一个字符
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/edit-distance
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
视频讲解:https://www.youtube.com/watch?v=MiqoA-yF-0M
1 | class Solution { |
7. 区间DP
概述:区间类动态规划是线性动态规划的扩展,它在分阶段地划分问题时,与阶段中元素出现的顺序和由前一阶段的哪些元素合并而来由很大的关系。令状态 $f(i,j)$ 表示将下标位置 $i$ 到 $j$ 的所有元素合并能获得的价值的最大(最小)值,那么 $f(i,j) = \max\{f(i,k) + f(k+1,j) + cost\}$,$cost$ 为将这两组元素合并起来的代价。
求解:对整个问题设最优值,枚举合并点,将问题分解为左右两个部分,最后合并两个部分的最优值得到原问题的最优值。
7.1 矩阵相乘的最少次数
https://onlinejudge.u-aizu.ac.jp/problems/ALDS1_10_B
本质上是在填一张表。
1 |
|
7.2 石子合并
https://vjudge.net/problem/51Nod-1021
1 |
|
7.3 Dire Wolf
杭电:http://acm.hdu.edu.cn/showproblem.php?pid=5115
1 |
|
7.4 Brackets
题目链接:https://vjudge.net/problem/POJ-2955
状态转移方程:当s[i]='(' \ and\ s[j]==')'\ or\ s[i]='[' \ and\ s[j]==']'
时,dp[i][j] = dp[i+1][j-1] + 2;
还有就是dp[i][j] = max(dp[i][j], dp[i][k] + dp[k+1][j]);
。
1 |
|