【1错笔记】最长递增子序列——一维动态规划

题目:

例题来源:传送

给定一个长度为n(1 <= n <= 1000)的整数序列a[i],求它的一个子序列(子序列即在原序列任意位置删除0或多个元素后的序列),满足如下条件:

1、该序列单调递增;

2、在所有满足条件1的序列中长度是最长的;


问题分析:

最近刚好在学习动态规划,不得不说DP实在太博大精深了,恰好看到这篇那么好的文章,就记录下其中一些例题的解题过程,也方便自己feedback

之前刷的DP都是二维的,今天这道题是一维的(其实这道题也可以转化为用最长公共子串二维来解,但要)

之前有详细讲解过最长公共子序列和最长回文子序列的解法,这里就不赘述了。

在理解完题意后,枚举固然是一种方法,只要把全部的可能性列出来然后逐一检查就能找到答案,但却不是可行的方法,因为2^(1000)是指数爆炸级别的。

嗯,既然枚举这种方法是肯定不行的,那么这种算法其中有没有我们值得学习和借鉴的呢?

有,通过这篇博客,我们可以大概把DP理解为记忆化的DFS,那么在枚举的过程中我们可不可以通过记忆减少复杂度呢?

因为在传统的枚举过程中,每次枚举的情况,我们都要从头开始算它到底有多少符合条件的子序列,这也是为什么枚举的复杂度是2^(1000)。那我们每一次枚举可不可以站在巨人的肩膀上,从上一次枚举的已经记录下来的结果推导出来呢?

那,这不就是DP的状态转移方程吗?

如何设计呢?建议看到这里的你先暂停一下,自己思考设计下状态转移方程。下面进行解密。

为了更形象一点,我先举个例子

假设有数列

1,4,6,3,8,2,9,11,2,60

肉眼就能判断出最长递增子序列是

1,4,6,8,9,11,60

我们一开始从第一个数开始检索,无论第一个数多大,最长递增子序列到目前为止就是1(因为就它一个呀,没有大小之分),到第二个,就要判断它是否比1大了,如果大,恭喜!加入子序列,反之则不加入。

假定我们用dp[i]来记忆截至到数列a[i]时的最长递增子序列

那么,就很容易推算出,当检索到a[j]的时候dp[j]的值肯定是dp[0]-dp[j-1]的值中最大的那个+1(当然要符合递增条件的前提下)

因为截至到每一个数时都有很多可能,比如当i为4时,他的子序列有

1,4,6,3

1,4,6

1,4,3

1,6,3

......

但其中最长的且符合条件的只有1,4,6

那么在i为5的时候,就可以看前面最大的dp是多少,然后如果i符合条件(递增条件)就+1,如果不符合条件,那它就没有任何价值了。


代码示例:

#include 
using namespace std;

int main()
{
	//测试用的数组 
	int a[]={1,4,6,3,8,2,9,11,2,60,1};
	int dp[10];
	int i,j;
	int len = sizeof (a) / sizeof (a[0]);
	

	//默认全部为1,就是以自己为起点 
	for ( i=0; ia[i] && dp[j]max ) max = dp[i]; 
				
	cout<

你可能感兴趣的:(刷题笔记,#,一级讲解)