codevs 1576 最长严格上升子序列

简介: 题目链接:http://codevs.cn/problem/1576/题目描述 Description给一个数组a1, a2 ... an,找到最长的上升降子序列ab1=0;i--)44 {45 printf("%d ",c[i]);46 }47 ...
题目链接:http://codevs.cn/problem/1576/
题目描述 Description

给一个数组a1, a2 ... an,找到最长的上升降子序列ab1<ab2< .. <abk,其中b1<b2<..bk。

输出长度即可。

输入描述 Input Description

第一行,一个整数N。

第二行 ,N个整数(N < = 5000)

输出描述 Output Description

输出K的极大值,即最长不下降子序列的长度

样例输入 Sample Input

5

9 3 6 2 7

样例输出 Sample Output

3

数据范围及提示 Data Size & Hint

【样例解释】

最长不下降子序列为3,6,7

解题思路

参考:北大郭炜老师

1.找子问题:“求以ak( k=1, 2, 3…N)为终点的最长上升子序列的长度”
一个上升子序列中最右边的那个数,称为该子序列的“终点”。
虽然这个子问题和原问题形式上并不完全一样,但是只要这N个子问题都解决了,那么这N个子问题的解中,最大的那个就是整个问题的解。

2. 确定状态
子问题只和一个变量-- 数字的位置相关。因此序列中数的位置k 就是“状态”,而状态 k 对应的“值”,就是以a[k]做为“终点”的最长上升子序列的长度。状态一共有N个。

3. 找出状态转移方程

maxLen [k]表示以a[k]做为“终点”的最长上升子序列的长度那么:
初始状态: maxLen [1] = 1
maxLen[k]= max { maxLen [i]: 1<=i < k 且 a[i ]< a[k]且 k≠1 } + 1
若找不到这样的i,则maxLen[k] = 1

maxLen[k]的值,就是在a[k]左边,“终点”数值小于a[k] ,且长度最大的那个上升子序列的长度再加1。因为a[k]左边任何“终点”小于a[k]的子序列,加上a[k]后就能形成一个更长的上升子序列 。

 1 #include <stdio.h>
 2 #define maxN 5005
 3 int n,a[maxN],maxLen[maxN];//maxLen[k]表示以a[k]做为“终点”的最长上升子序列的长度
 4 int main(int argc, char *argv[])
 5 {
 6     int i,j;
 7     scanf("%d",&n);
 8     for(i=0;i<n;i++) {  scanf("%d",&a[i]); maxLen[i]=1;  }
 9     
10     for(i=1;i<n;i++)//枚举所有子序列的终点 
11     {
12         for(j=0;j<i;j++)//枚举以a[i]做终点的子序列中a[i]的前缀元素 
13         {
14             if(a[j]<a[i])//尝试用a[j]做a[i]的直接前缀形成新的子序列 
15             {
16                 maxLen[i]=(maxLen[j]+1>maxLen[i]?maxLen[j]+1:maxLen[i]);
17             }
18         }
19     }
20     printf("%d\n",maxLen[n-1]);
21     return 0;
22 }

上面的代码写错了,抱歉。更正如下:

 1 #include <stdio.h>
 2 #define maxN 5005
 3 int main(int argc, char *argv[])
 4 {
 5     int i,j,t;
 6     int n,a[maxN],maxLen[maxN];//maxLen[k]表示以a[k]做为“终点”的最长上升子序列的长度
 7     int max;
 8     
 9     scanf("%d",&n);
10     for(i=0;i<n;i++) {  scanf("%d",&a[i]); maxLen[i]=1;  }
11     for(i=1;i<n;i++)//枚举所有子序列的终点 
12     {
13         for(j=0;j<i;j++)//枚举以a[i]做终点的子序列中a[i]的前缀元素 
14         {
15             if(a[j]<a[i])//尝试用a[j]做a[i]的直接前缀形成新的子序列 
16             {
17                 maxLen[i]=(maxLen[j]+1>maxLen[i]?maxLen[j]+1:maxLen[i]);
18             }
19         }
20     }
21     max=maxLen[0];
22     for(i=1;i<n;i++)
23         if(maxLen[i]>max) max=maxLen[i];
24     printf("%d\n",max);
25     return 0;
26 }

 

思考题 : 如何改进程序,使之能够输出最长上升子序列 ?

思路:新增pre[ ],其中pre[k]=x表示在a[ ]序列构成的若干个上升子序列中,a[k[的前驱是a[x]。一开始pre[ ]全部初始化为-1表示一开始所有元素的前驱都是自己本身。在循环求解maxLen[i]的同时,更新pre[i]。最后在扫描出maxLen[ ]最大值为maxLen[i]以后,从pre[i]往前推即可。假如要顺序输出该最长上升子序列,可以把逆推pre[ ]的过程保存再输出。

参考代码:

 1 #include<stdio.h>
 2 #include<string.h>
 3 #define maxN 5005
 4 int main(int argc, char *argv[])
 5 {
 6     int i,j,t;
 7     int n,a[maxN],maxLen[maxN];//maxLen[k]表示以a[k]做为“终点”的最长上升子序列的长度
 8     int max;
 9     int pre[maxN];
10     int c[maxN],maxIndex;
11     
12     memset(pre,-1,sizeof(pre));
13     
14     scanf("%d",&n);
15     for(i=0;i<n;i++) {  scanf("%d",&a[i]); maxLen[i]=1;  }
16     
17     for(i=1;i<n;i++)//枚举所有子序列的终点 
18     {
19         for(j=0;j<i;j++)//枚举以a[i]做终点的子序列中a[i]的前缀元素 
20         {
21             if(a[j]<a[i])//尝试用a[j]做a[i]的直接前缀形成新的子序列 
22             {
23                 if(maxLen[j]+1>maxLen[i])
24                 {
25                     maxLen[i]=maxLen[j]+1;
26                     pre[i]=j;
27                 }
28             }
29         }
30     }
31     max=maxLen[0];
32     for(i=1;i<n;i++)
33         if(maxLen[i]>max) { max=maxLen[i]; maxIndex=i; }
34     printf("%d\n",max);
35     
36     j=0;
37     c[j++]=a[maxIndex];
38     while(pre[maxIndex]!=-1)
39     {
40         maxIndex=pre[maxIndex];
41         c[j++]=a[maxIndex];
42     }
43     for(i=j-1;i>=0;i--)
44     {
45         printf("%d ",c[i]);
46     }
47     printf("\n");
48     return 0;
49 }
View Code

 

相关文章
|
机器学习/深度学习 人工智能 搜索推荐
《百炼成金-大金融模型新篇章》––09.金融级AI原生的发展
百炼必定成金,新质生产力会催生新质劳动力,谨以此文抛砖引玉,希望与业内的各位朋友一同探讨如何积极拥抱并运用大模型技术,以应对和驾驭不断变化的市场环境,实现科技金融持续稳定的提质增效和创新发展,携手开启金融大模型未来新篇章。
257 3
|
安全 编译器 程序员
全面解析C++11新特性:现代编程的新起点(上)
全面解析C++11新特性:现代编程的新起点
全面解析C++11新特性:现代编程的新起点(上)
|
KVM 虚拟化
KVM虚拟机的冷迁移
这篇文章详细描述了KVM虚拟机的冷迁移过程,包括无依赖环境迁移、有链接克隆虚拟机迁移、多块磁盘迁移的案例,以及可能遇到的错误和解决方案。
437 3
|
机器学习/深度学习 算法 数据挖掘
R语言中的支持向量机(SVM)与K最近邻(KNN)算法实现与应用
【9月更文挑战第2天】无论是支持向量机还是K最近邻算法,都是机器学习中非常重要的分类算法。它们在R语言中的实现相对简单,但各有其优缺点和适用场景。在实际应用中,应根据数据的特性、任务的需求以及计算资源的限制来选择合适的算法。通过不断地实践和探索,我们可以更好地掌握这些算法并应用到实际的数据分析和机器学习任务中。
|
存储 安全 JavaScript
Weaver E-Office v9.5 文件上传(CVE-2023-2648)
Weaver E-Office v9.5 文件上传(CVE-2023-2648)
|
移动开发 数据安全/隐私保护 Python
100行代码手把手带你实现Feisitel加密算法
Feistel 加密算法,或者叫做 Feistel 网络,是一种块加密(block cipher)模型,很多常见的加密算法都具有 Feistel 结构,如 DES、blowfish 等。 Feistel 将明文分割成固定大小(block size)的块(如 32bit、64bit),然后对于每个块进行若干轮操作,每轮操作需要用到一个 key,因此总计需要循环轮数个 key。解密时需要用相同的 keys,因此这是一种对称加密算法。
|
Java 调度
代码打造每日任务系统
在游戏开发中,每日任务系统对提升玩家活跃度和留存率至关重要。通过Java的面向对象特性,可将每日任务抽象为`Task`类,并通过实例化及方法调用实现任务创建、执行与奖励功能。进一步,可以创建`DailyTaskSystem`类来管理所有每日任务,包括添加、删除和获取任务列表等操作。这种设计不仅简化了任务管理,还增强了游戏的可玩性和吸引力。更多细节和实现方法可见相关游戏逻辑设计与具体需求。
183 0
|
存储 云安全 安全
云端数据加密实践
【7月更文挑战第12天】云端数据加密是保障云端数据安全的重要手段。通过选择合适的加密方式、加强加密密钥管理、实施静态与动态数据加密、采用加密信息检索技术和应用层加密组件等措施,可以有效地保护云端数据的安全。未来,随着技术的不断进步和应用需求的多样化,云端数据加密技术将继续发挥其重要作用,为各种应用场景提供强大的安全保障。
|
自然语言处理 算法 API
「AIGC」Python实现tokens算法
使用Python的`transformers`库,通过`AutoTokenizer`初始化BERT tokenizer,对文本进行分词统计,减少API调用。示例展示从开始到结束的时间,包括文本转换为tokens的数量和过程耗时。
382 0
「AIGC」Python实现tokens算法
|
JSON 前端开发 数据格式
Cesium案例解析(十)——CZML点
Cesium案例解析(十)——CZML点
273 0