1.O(n^2)LIS模板
dp[i]代表: 必须以a[i]为结尾的LIS的长度.#include <iostream> #include<cstring> using namespace std; const int nmax=1000+5; int a[nmax];//存储数值序列 int dp[nmax];//dp[i]记录必须以a[i]为结尾的LIS的长度 int main(int argc, char** argv) { int n; while(cin>>n){ for(int i=0;i<n;i++){ cin>>a[i]; dp[i]=1; } int ans=1;//ans记录整个序列的LIS长度,初值为1 for(int i=1;i<n;i++){ for(int j=0;j<i;j++){ if(a[j]<a[i]){ dp[i]=max(dp[j]+1,dp[i]); } ans=max(ans,dp[i]); } } cout<<ans<<endl; } return 0; }
2.1 O(nlogn)LIS模板(简化版——使用lower_bound)
dp[i]代表: 假设当前遍历到某个数cur,遍历到当前时刻为止,长度为i+1的LIS的最小末尾
#include <iostream> #include<cstring> using namespace std; const int nmax=1000+5; int a[nmax];//存储数值序列 int dp[nmax];//dp[i]记录必须以a[i]为结尾的LIS的长度 const int INF=0x3f3f3f; int main(int argc, char** argv) { int n; while(cin>>n){ for(int i=0;i<n;i++){ cin>>a[i]; dp[i]=INF;//不要设置为1,因为更新时会产生dp[i]为1的情况 } for(int i=0;i<n;i++){ *lower_bound(dp,dp+n,a[i])=a[i];//在dp数组位置0~n区间内找第一个大于a[i]的数,并用a[i]替换; } cout<<lower_bound(dp,dp+n,INF)-dp<<endl;//找到第一个INF的地址减去首地址就是最大子序列的长度; } return 0; }
2.2 O(nlogn)LIS模板(自己实现了二分查找的过程)
dp[i]代表: 假设当前遍历到某个数cur,遍历到当前时刻为止,长度为i+1的LIS的最小末尾
#include <iostream> #include<cstring> using namespace std; const int nmax=1000+5; int a[nmax];//存储数值序列 int dp[nmax];//dp[i]记录遍历到当前时刻为止,长度为i+1的LIS的最小末尾 //int ends[nmax];//有效区数组 const int INF=0x3f3f3f; int main(int argc, char** argv) { int n; while(cin>>n){ for(int i=0;i<n;i++){ cin>>a[i]; } int* ends=new int[n];//有效区数组 ends[0]=a[0];//直接拷贝数组中第一个元素 dp[0]=1; int right=0;//有效区的右边界 //在有效区里二分查找用到的变量 int l=0;//左查找边界 int r=0;//右查找边界 int m=0;//中间查找的位置 for(int i=1;i<n;i++){//有效区插入时保证有序 l=0; r=right; while(l<=r){ m=(l+r)/2; if(ends[m]<a[i]){ l=m+1;//如果最终没找到,有效区右边界扩充1个单位 } else{//ends[m]>=a[i] r=m-1; } } right=max(right,l);//如果没找到,有效区右边界扩充1个单位 //如果找到了,右边界仍然为right ends[l]=a[i];//若有效区中没找到大于a[i]的数,更新新扩充的右边界 //若有效区中找到大于a[i]的数,将该数改写成a[i] dp[i]=l+1; } int ans=1; for(int i=0;i<n;i++){//dp[i]已经记录了LIS的最小末尾,遍历一遍求最值 ans=max(ans,dp[i]); } cout<<ans<<endl; } return 0; }