当前位置:首页 > 题解目录 > 正文内容

【题解】求最长不下降序列

亿万年的星光2年前 (2023-05-05)题解目录1613

【题目描述】

设有由n(1≤n≤200)个不相同的整数组成的数列,记为:b(1)、b(2)、……、b(n)且b(i)≠b(j)(i≠j),若存在i1<i2<i3<…<ie 且有b(i1)<b(i2)<…<b(ie)则称为长度为e的不下降序列。程序要求,当原数列出之后,求出最长的不下降序列的长度。

例如13,7,9,16,38,24,37,18,44,19,21,22,63,15。

例中13,16,18,19,21,22,63就是一个长度为7的不下降序列,同时也有7 ,9,16,18,19,21,22,63组成的长度为8的不下降序列。

【输入描述】

第一行为n,第二行为用空格隔开的n个整数。

【输出描述】

第一行为输出最大个数max

【样例输入1】

14
13 7 9 16 38 24 37 18 44 19 21 22 63 15

【样例输出1】

max=8

【样例输入2】

7
1 7 3 5 9 4 8

【样例输出2】

max=4




【题目分析】

  • 一维数组dp问题。

  • 我们采用'从前往后'推理的方式进行分析。依序求出以原序列中各元素分别为子序列结尾情况下能得出的最长不下降子序列。我们先给上面的数字的一部分进行分析(为了后面分析方便,我们给数字加入编号)



  • 数字13791638243718
    编号12345678

    (1)以编号为1的子序列作为结尾,那么只包含1号元素本身。最长长度:1

    (2)以编号为2的子序列作为结尾,那么也只能包含2号元素本身。最长长度:1

    (3)以编号为3的子序列作为结尾,那么包含2号和3号(7,9)。最长长度:2

    (4)以编号为4的子序列作为结尾,那么包含2,3,4号(7,9,16)。最长长度:3

    (5)以编号为5的子序列作为结尾,那么包含2,3,4,5号(7,9,16,38)。最长长度:4

      (6)  以编号为6的子序列作为结尾,那么包含2,3,4,6号(7,9,16,24)。最长长度:4

    (7)以编号为7的子序列作为结尾,那么包含2,3,4,6,7号(7,9,16,24,37)。最长长度:5

    (8)以编号为8的子序列作为结尾,那么包含2,3,4,8(7,9,16,18)号。最长长度:4

那么我们可以把上面的表格扩展成下面这个样子:

数字13791638243718
编号12345678
长度11234454

从表格中可以看出,最长长度是5。那么这个最长长度是如何分析出来的呢?

其实从上面的过程中就可以看出,每个元素的长度都是经过比较得出来的。

一开始,所有的数字编号的长度都为1(每个元素都算是一个长度)。我们用dp数组保存,那么就是dp[i]=1。

然后开始比较和筛选的过程。

那么比较的是什么?比较的是数字的大小。也就是要大于等于前者,这个时候才要进行判断,是否更改当前编号的长度,如果不更换就是dp[i]不变。那么基本框架如下:

//外循环表示我们以每一个数都作为子序列结尾进行求解 
for(int i=1; i<=n; i++) {
	dp[i]=1;  //用dp数组表示每个数字的长度,一开始每个数字都是1  (注意:不一定要全部初始化为1,只要能正确表示后续长度即可)
	
	//内循环到i-1终止,找出符合条件的数 
	for(int j=1; j<i; j++) {
	    
	}
}

那么内部循环里面实际上就分成两种情况。要不当前遍历的这个数dp[i]要么不变(dp[i]本身),要么是内循环中大于等于当前数的长度加1

那么什么时候需要进行dp数组长度的更新呢,可以从上面的表格中看出。当后者比前者大时,需要判断和更新。

也就是下面这样:

//外循环表示我们以每一个数都作为子序列结尾进行求解 
for(int i=1; i<=n; i++) {
	dp[i]=1;  //用dp数组表示每个数字的长度,一开始每个数字都是1
	
	//内循环到i-1终止,找出符合条件的数 
	for(int j=1; j<i; j++) {
		
		//因为j最大是i-1, 所以是a[i]>=a[j] 
		if(a[i]>=a[j]){
			//如果后者比前者大,那么考虑更新当前值dp[i] 
		} 
	}
}

那么就可以得出结论,dp[i]的值,要么跟dp[i]相等,要么是dp[j]+1。

比如我们在分析6号元素时,此时外循环a[6]=24, 内循环a[j]从13一直到38。
但是当j=1,2,3,4时,满足条件a[i]>=a[j], 那么dp[i]就会被不断的更新。
j=1时,d[j]=1, d[j]+1=2, 取d[j]+1
j=2时,d[j]=1, d[j]+1=2, 取d[j]+1
j=3时,d[j]=2, d[j]+1=3, 取d[j]+1
j=4时,d[j]=3, d[j]+1=4, 取d[j]+1

 可以看出,我们并没有取到过d[j],其实很简单,我们把a[5]=38改成a[5]=24,


数字1379162424
编号123456
长度112344

那么在比较的过程中就可以得到

i=5
j=4
a[5]>=a[4] 不成立
此时 dp[i]=1,d[j]+1=5, 此时取d[i]

总结:

//外循环表示我们以每一个数都作为子序列结尾进行求解 
for(int i=1; i<=n; i++) {
	dp[i]=1;  //用dp数组表示每个数字的长度,一开始每个数字都是1
	
	//内循环到i-1终止,找出符合条件的数 
	for(int j=1; j<i; j++) {
		
		//因为j最大是i-1, 所以是a[i]>=a[j] 
		if(a[i]>=a[j]){
			//如果后者比前者大,那么考虑更新当前值dp[i] 
			dp[i]=max(dp[i],dp[j]+1) 
		} 
	}
}

        其实动态规划就是一个填表的过程,上一个阶段的结果作为求下一个阶段求解的依据。针对这道题目,当我们求出以每个元素为起点或者终点的最长不下降子序列的长度之后,我们还需要找出整个长度行中的最大值,才能找到整个序列的最长不下降子序列长度。


【参考代码】

#include<bits/stdc++.h> 
using namespace std;
int a[1005],dp[1005];
int main()
{
    int n;
    int maxx=-9999;
    cin>>n;
    for(int i=1;i<=n;i++)
        cin>>a[i];
    for(int i=1;i<=n;i++)
    {
        dp[i]=1;
        for(int j=1;j<i;j++)
            if(a[j]<=a[i]){
            	dp[i]=max(dp[i],dp[j]+1);
			}
        if(dp[i]>maxx)
        {
            maxx=dp[i];
        }
    }
    cout<<"max="<<maxx<<endl; 
    return 0;
}




扫描二维码推送至手机访问。

版权声明:本文由青少年编程知识记录发布,如需转载请注明出处。

标签: 动态规划dp
分享给朋友:

相关文章

【题解】校运会

【题解】校运会

【题目描述】校运会上,一共有N个参赛选手,已知N个选手的名字。然后老师告诉你M句话,话的内容是学生A与学生B在同一组里。如果学生A与学生B在同一组里,学生B与学生C也在同一组里,就说明学生A与学生C在...

【题解】报数游戏

【题目描述】路飞在和他朋友们一块玩一个游戏。由于路飞的机智,这个游戏由路飞担任裁判。首先,路飞会给他们一个人一个编号,并且每个人的编号都不相同。接下来的每一个回合,会给一个数,编号不超过它的最大编号的...

【题解】剔除相关数

【题目描述】一个数与另一个数如果含有相同数字和个数的字符,则称两数相关。现有一堆乱七八糟的整数,里面可能充满了彼此相关的数,请你用一下手段,自动地将其剔除。【输入描述】每组数据前有一个N(<10...

字符全排列(2)

【题目描述】从n个字符(n从a开始,依次递增)中选取r个字符,对r个字符进行不重复排列。字典序小的在前面。【输入描述】一行,n和r【输出描述】r个字符的所有组合,每种组合占一行,字符和字符之间用空格隔...

【题解】踩方格

【题目描述】有一个方格矩阵,矩阵边界在无穷远处。我们做如下假设:a、每走一步时,只能从当前方格移动一格,走到某个相邻的方格上;b、走过的格子立即塌陷无法再走第二次;c、只能向北、东、西三个方向走;请问...

【题解】01串

【题目描述】Fans是个ACM程序设计迷。有时侯,他表现出很强烈的逆反心理,你往东,他往西,你往南,他偏往北。这一次,不知道又是谁惹着他了,好端端的一个个01串,到了他的手里,都变成10串了。请你编个...