POJ 2533 &FJUT 1388最长上升子序列(LIS)
最长上升子序列
如果一个数列ai 满足 a1 < a2 < ... < aN 则这个数列被称作上升序列。
给定一个数列a(a1, a2, ..., aN)则任意一个数列b(ai1, ai2, ..., aiK)并且满足(1 <= i1 < i2 < ... < iK <= N).则b被称为a的子序列。
如果一个数列的子序列是上升序列,则这个序列称为原序列的上升子序列。
比如序列(1, 7, 3, 5, 9, 4, 8)的上升子序列有(1, 7), (3, 4, 8)等. 它的最长上升子序列长度是4,即(1, 3, 5, 8).
请你写一个程序求一个序列的最长上升子序列的长度。
Input
第一行是一个整数N表示给定数列的长度. 第二行包括N个范围在0~10000的整数。 1 <= N <= 1000
Output
输出一个整数表示最长上升子序列的最大长度
SampleInput
7 1 7 3 5 9 4 8
SampleOutput
4
方法1:(复杂度O(n²))
思路:dp[i]表示以dp[i]结尾的最长上升子序列
每次都向前找比它小的数和比它大的数的位置,将第一个比它大的替换掉,
前面比他大的可能是a0,a1,a3... ak (0<=k<i)也可能是是空集,空集的话dp【i】=1;
状态转移:dp[ i ] = max { dp [ j ] + 1 ,dp [ i ] } (0 <= j < i,A[ j ] < A[ i ])
代码:
#include<iostream> #include<algorithm> using namespace std; int i,j,n,a[1005],dp[1005],max1; int main() { cin>>n; for(i=0; i<n; i++) cin>>a[i]; dp[0]=1; //初始化,以a[0]结尾的最长递增子序列长度为1 for(i=1; i<n; i++) { dp[i]=1;//b[i]最小值为1(空集状态) for(j=0; j<i; j++) if(a[i]>a[j]) dp[i]=max(dp[i],dp[j]+1); } for(int i=0;i<n;i++) { max1=max(max1,dp[i]); } cout<<max1<<endl; }
方法二:复杂度:O(nlogn)
思路:首先数组a中存输入的数,一个数组b表示所有长度为i的上升子序列中,结尾元素的最小值。
要最长上升子序列最长,末尾的值要尽可能小,因为小的值比大的值更容易创造更长的子序列。显然b数组是递增的
对于每一个a[i],可以通过二分在数组b中查找第一个大于等于a[i]的元素,,就可以用a[i]来代替它,也就是较小的数吧大于等于他的数替换掉了,且每次替换后所得结果不会更少。
#include<string> #include<iostream> #include<algorithm> #include<queue> #include<cstdio> #include<cstring> #include<cstdlib> #include<map> using namespace std; int n; int a[1005]; int b[1005]; int main() { cin>>n; for(int i=0; i<n; i++) { cin>>a[i]; b[i]=1e9; } int len=0;//表示当前长度 for(int i=0; i<n; i++) { int j=lower_bound(b,b+n,a[i])-b;//找到属于他的位置 b[j]=a[i];//替换掉第一个大于或者等于这个数字的那个数 len=max(len,j+1);//当前下标+1,因为数组从0开始,长度默认为1 } cout<<len<<endl; return 0; }

///题意:怪盗基德可以选择其中任意一个建筑作为起点, ///朝一个方向向高度低的建筑物滑翔,求其能经过的建筑物的最大数量 ///思路:正向和逆向分别求LIS长度,找出二者中的较大值。 #include<iostream> #include<algorithm> using namespace std; int i,j,n,a[1005],dp[1005],maxx; int main() { int T; cin>>T; while(T--) { cin>>n; for(i=1;i<=n;i++)cin>>a[i]; maxx=0; //正向求解 for(i=1; i<=n; i++) { dp[i]=1; for(j=1; j<i; j++) { if(a[i]>a[j]) { dp[i]=max(dp[i],dp[j]+1); } } maxx=max(maxx,dp[i]); } //逆向求解 for(i=n; i>=1; i--) { dp[i]=1; for(j=n; j>i; j--) { if(a[i]>a[j]) { dp[i]=max(dp[i],dp[j]+1); } } maxx=max(maxx,dp[i]); } cout<<maxx<<endl; } }
例题2:HDU 1087&FJUT 1379: Super Jumping! Jumping! Jumping!

///题意:求最大上升子序列和 ///思路:dp[i],表示以a[i]结尾的最大递增子段和 ///dp[i]=max(dp[j]+a[i],dp[i])(j<i) dp[j]若为空集则为a[i] #include<iostream> #include<algorithm> #include<cstring> using namespace std; int i,j,n,a[1005],b[1005],max1; int main() { while( cin>>n,n) { max1=0; memset(b,0,sizeof(b)); for(i=0; i<n; i++) cin>>a[i]; for(i=0; i<n; i++) { b[i]=a[i]; for(j=0; j<i; j++) { if(a[i]>a[j]) b[i]=max(b[i],b[j]+a[i]); } max1=max(max1,b[i]); } cout<<max1<<endl; } }

#include<iostream> #include<algorithm> #include<cstring> ///题意:1.编号递增:子序列 2.相邻两个景点不能相同 3.下山后不能上山了 2&3:先单调增再单调减的序列的最大值 ///思路:正向逆向分别求LIS,最后相加求max(dp1[i],dp2[i]) using namespace std; int dp1[1005]; int a[1005]; int dp2[1005]; int main() { int n; cin>>n; for(int i=0;i<n;i++) { cin>>a[i]; } for(int i=0;i<n;i++) { dp1[i]=1; for(int j=0;j<i;j++) { if(a[j]<a[i]) { dp1[i]=max(dp1[i],dp1[j]+1); } } } for(int i=n-1;i>=0;i--) { dp2[i]=1; for(int j=n-1;j>i;j--) { if(a[j]<a[i]) { dp2[i]=max(dp2[i],dp2[j]+1); } } } int ans=0; for(int i=0;i<n;i++) { ans=max(ans,dp1[i]+dp2[i]-1); } cout<<ans<<endl; return 0; }
View Code

/////题意:河两岸有不同的城市,每个城市分别都有且只有一个友好城市,求要在友好城市之间建桥如何建的桥最多且不想交。 //其实就是 有n条直线,求最多可以有多少条直线互不相交。 //思路:将直线的一头看做数组中的i,将另一头看做a[i],每个i对应一个a[i]。 //如果要最多直线且不想交,a[i]的肯定是要递增(如果有不是递增的话就会出现交叉),如a1>=a2&b1>=b2(其中a,b表示两岸); //所以将一头排序,另一头求最长上升子序列。 //这题洛谷的要用二分才能过。 #include<iostream> #include<algorithm> #include<cstring> using namespace std; typedef pair<int,int> PII; int b[200005]; PII a[200005]; int main() { int n; cin>>n; for(int i=0; i<n; i++) { cin>>a[i].first>>a[i].second; b[i]=1e9; } sort(a,a+n);//pair 默认对first升序,当first相同时对second升序; int ans=0; for(int i=0;i<n;i++) { int j=lower_bound(b,b+n,a[i].second)-b; b[j]=a[i].second; ans=max(ans,j+1); } cout<<ans<<endl; return 0; }
例题6:FJUT 1384&HDU 1160:FatMouse's Speed

//题意:要求求出体重的升序,速度的降序求出最长的子序列,并从头到尾输出路径。 //思路:结构体排序,按照题目要求排序,然后套LIS,更新的时候记录前缀,最后输出路径用递归或者栈 #include <iostream> #include<cstring> #include<algorithm> #include<cstdio> #include<stack> using namespace std;const int N = 2005; int dp[N]; int way[N]; int n; int i,j,cnt; stack<int> s; struct node { int x,y,id; } a[N]; void print(int pos) { // if(pos==-1) return ; // print(way[pos]); // cout<<a[pos].id<<endl; while(~pos) { s.push(a[pos].id); pos=way[pos]; } while(s.size()) { cout<<s.top()<<endl; s.pop(); } } bool cmp(node a,node b) { if( a.x!=b.x) { return a.x<b.x; } return a.y<b.y; } int main() { cnt=0; while(~scanf("%d%d",&a[cnt].x,&a[cnt].y)) { a[cnt].id=cnt+1; cnt++; } sort(a,a+cnt,cmp);memset(way,-1,sizeof(way)); int pos=0; int len=0; for(int i=0; i<cnt; i++) { dp[i]=1; for(int j=0; j<i; j++) { if(a[i].x>a[j].x&&a[i].y<a[j].y&&dp[i]<dp[j]+1) { dp[i]=dp[j]+1; way[i]=j; } } if(len<dp[i]) { len=dp[i]; pos=i; } } cout<<len<<endl; print(pos); return 0; }
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 单元测试从入门到精通
· 上周热点回顾(3.3-3.9)
· winform 绘制太阳,地球,月球 运作规律