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

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

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

【题目描述】

设有由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
分享给朋友:

相关文章

【题解】车辆管理

【题目描述】交通管理局长氓氓现在需要一个管理汽车的系统,每一辆汽车都有许多信息需要去记录。 首先,每一辆汽车都有一个独一无二的车牌号 S,车牌号由 7 个字符组成。 然后,对于每一辆车要记录它的排...

【题解】队列问题

【题解】队列问题

4.队列问题(lru.cpp)【题目描述】有一个大小为n的页面缓存队列,初始为空,当计算机访问页面时,若缓存队列没有该页面,则加入到缓存队列中,若队列已满,则将删除访问时间最远的页面。有Q次询问,每次...

【题解】切比雪夫距离

【题目描述】小C有一个平面!它发现了平面上的两个点,请你求出求它们之间的切比雪夫距离。切比雪夫距离定义为x与y方向坐标差的绝对值较大值。【输入描述】四个整数,a,b,c,d。坐标为(a,b)与(c,d...

【题解】游览动物园

【题目描述】动物园有很多游览区,小红已经在动物园的一个游览区游览,突然接到电话,要半个小时内到动物园外面跟一个朋友见面。半个小时小红只够游览完当前区域之后,游览一个最近的景区。已知从一个游览区域只能沿...

【题解】括号匹配问题

【题目描述】在某个字符串(长度不超过100)中有左括号、右括号和大小写字母;规定(与常见的算数式子一样)任何一个左括号都从内到外与在它右边且距离最近的右括号匹配。写一个程序,找到无法匹配的左括号和右括...

【题解】冒泡排序计数

【题目描述】考虑冒泡排序的一种实现。bubble-sort  (A[],  n)>   round  =  0>   while...