最长上升/不下降子序列
最长上升/不下降子序列:(LIS)
有两种方法:
1.动态规划,O(n^2)
容易得出O(n^2)的DP递推公式:D[i]=max{D[j]}+1;(1<=j<i且E[j]<E[i])
D[i]为以元素i结尾的最长子序列个数。
这样经过两重循环一次遍历可以得到最长上升子序列。
代码:
View Code
1 #include <iostream> 2 #include <stdio.h> 3 #include <cstring> 4 using namespace std; 5 const int maxnum=100; 6 int a[maxnum],dp[maxnum]; 7 8 int main() 9 { 10 int n,i,j; 11 scanf("%d",&n); 12 for(i=0;i<n;i++) 13 { 14 scanf("%d",&a[i]); 15 dp[i]=1; 16 } 17 18 for(i=1;i<n;i++) 19 for(j=0;j<i;j++) 20 if(a[j]<=a[i]) 21 dp[i]=max(dp[i],dp[j]+1); 22 23 int ans=dp[0]; 24 cout<<dp[0]<<" "; 25 for(i=1;i<n;i++) 26 { 27 cout<<dp[i]<<" "; 28 if(ans<dp[i]) 29 ans=dp[i]; 30 } 31 cout<<endl; 32 printf("%d!!\n",ans); 33 return 0; 34 } 35 /* 36 7 37 3 2 5 4 4 6 7 38 */
2.二分查找法,CMI算法,O(nlogn)
操作:
开辟一个栈b,每次取栈顶元素s和读到的元素a做比较,如果a>s,则置为栈顶;如果a<s,则二分查找栈中的比a大的第1个数,并替换。最终栈的大小即为最长递增子序列为长度。
考察b栈内每个元素的含义,b[i] 表示所有长度为i的上升子序列中最后一个数最小的
举例:原序列为3,4,5,2,4,2
栈为3,4,5,此时读到2,则用2替换3,得到栈中元素为2,4,5,再读4,用4替换5,得到2,4,4,再读2,得到最终栈为2,2,4,最终得到的解是:
长度为1的上升子序列中最小的最后一个数是2 (2)
长度为2的上升子序列中最小的最后一个数是2 (2,2)长度为3的上升子序列中最小的最后一个数是4 (3,4,4)
可知没有长度为4的上升子序列,最长递增子序列长度为3. (3,4,4)
CMI本质是LIS问题的另一种动态规划思路
注意:CMI只能求LIS的长度和最后一个数,不能求LIS的序列!
代码:
View Code
1 #include <iostream> 2 #include <stdio.h> 3 #include <cstring> 4 using namespace std; 5 const int maxnum=100; 6 int a[maxnum]; 7 int stack[maxnum]; 8 int top=-1; 9 10 void function(int cur) 11 { 12 int l=0,r=top; 13 while(l<=r) 14 { 15 int m=(l+r)>>1; 16 if(stack[m]>=cur) //递增,stack[m]>cur为非递减 17 r=m-1; 18 else 19 l=m+1; 20 } 21 if(l>top) 22 top=l; 23 stack[l]=cur; 24 } 25 26 int main() 27 { 28 int n,i; 29 scanf("%d",&n); 30 for(i=0;i<n;i++) 31 { 32 scanf("%d",&a[i]); 33 function(a[i]); 34 } 35 printf("%d\n",top+1); 36 return 0; 37 } 38 39 /* 40 6 41 3 4 5 2 4 2 42 */