dp部分一:线性动态规划算法
知识概述
动态规划算法(简称dp)是算法设计中的重要组成部分,往往在求解全局最优或统计数量的时候使用。动态规划算法的学习历程就可以与贪心算法学习类比,不同的是贪心算法的求解目的是局部最优解。即在进行算法求解时只能保证求出的解在已知的约束条件下是成立的,某些复杂问题使用贪心算法可能会求出多个不同的局部最优解,难以判断哪个才是全局最优解。动态规划问题的求解则是递推的求出全局最优解,利用全局最优解,一定包含全局最优子解这种思路。对要求解的问题进行多步的简化和分解,最终落实到一个递推式的生成上。
动态规划的算法实质理解清楚之后,可以发现这种算法并不是具体的明文算法,而是类似贪心、分治的思维算法。利用最优解包含最优子解的原理,逻辑推理出:在求一个最优解时,只有过去求得的解会影响该解,未来求得的解不会。利用这个推论,可以将新解的求得表示为多个已知解的递推式。整个求解过程中,由于保持了每一步都是基于已知量采取的最优策略,可以保证每个解的求得都是最优的,进而求得了全局最优解。
综上,可以发现在动态规划问题中,两个问题的求解至关重要:初始状态和状态转移递推关系式。解决一个动态规划的求解问题时,寻找到准确的初态和有效的状态转移关系,是十分高效的通道。
看完这些,可能你对动态规划已经有所了解,下面用三道经典的动态规划问题来举例说明动态规划的求解。
题目一
题目概述
东东有两个序列A和B。
他想要知道序列A的LIS和序列AB的LCS的长度。
注意,LIS为严格递增的,即a1<a2<…<ak(ai<=1,000,000,000)。
INPUT&输入样例
第一行两个数n,m(1<=n<=5,000,1<=m<=5,000)
第二行n个数,表示序列A
第三行m个数,表示序列B
输入样例:
5 5
1 3 2 5 4
2 4 3 1 5
OUTPUT&输出样例
输出一行数据ans1和ans2,分别代表序列A的LIS和序列AB的LCS的长度
输出样例:
3 2
题目重述
题目综合了两个线性动态规划的基本问题:最长上升子序列(LIS)和最长公共子序列(LCS)。对A序列求解LIS,对AB求解器最长的公共子序列。
最长上升子序列定义:在一个序列中,依次选取出一个子序列,且保证选出的子序列严格有序(a1<a2<a3<…<ak),则称为该序列为上升子序列。最长上升子序列就是所有序列中,选取的子序列长度最大的。
最长公共子序列定义:在两个序列中,如果有部分子序列数值和顺序是完全一致的,则称为公共子序列。最长公共子序列就是两个序列中最长的公共序列。
思路概述
LIS和LCS是线性动态规划的经典问题,在学习dp的过程中是不可不做的例题。
LIS求解
LIS问题求解思路:
LIS的序列求解要求有两个:1、保持序列的顺序 2、数值大小的顺序
为了保证这两个要求都能够满足,将要求的dp解定义为:
dp[i]—>以Ai为结尾的最长子序列的长度
经过思考,我们可以写出如下的状态转移表达式
dp[1]=1
dp[i]=max{dp[j] | j<i && Aj<Ai}+1
简单的递推求解即可求出全部最优解。
LCS 求解
LCS问题求解思路:
LCS的核心要求是求出相对于两个序列的最长公共子序列
为了满足两个序列的要求,使用二维dp解来存储序列长度:
dp[i][j]—>到A序列的i位置B序列的j位置最长的公共子序列
状态转移转换考虑两种情况即可:
A和B序列 i 和 j 位置值相等—>dp[i][j]=dp[i-1][j-1]+1
A和B序列 i 和 j 位置值不等—>dp[i][j]=max(dp[i][j-1],dp[i-1][j])
同样只需要简单的递推即可求出全局最优解。
源码(C++)
#include<iostream>
#include<stdio.h>
#include<algorithm>
using namespace std;
const int M=1e4;
int alist[M];
int blist[M];
int dplis[M];
int dplcs[M][M];
int lis(int m)
{
dplis[1]=1;
int max_res