给出长度为N的数组,找出这个数组的最长递增子序列。(递增子序列是指,子序列的元素是递增的)
例如:5 1 6 8 2 4 5 10,最长递增子序列是1 2 4 5 10。
Input
第1行:1个数N,N为序列的长度(2 <= N <= 50000) 第2 - N + 1行:每行1个数,对应序列的元素(-10^9 <= S[i] <= 10^9)
Output
输出最长递增子序列的长度。
Input示例
8 5 1 6 8 2 4 5 10
Output示例
5
题意:最经典的最长上升子序列问题,给出了你一个包含n个数的最长上升子序列,要你求出在这个序列中,单调递增的子序列最长为多少?例如:1 9 4 3 5 2 6 7的最长上升子序列为1 3 5 6 7或1 4 5 6 7。
思路:最长上升子序列有几种解法(菜鸟的我只会两种),一种最简单的dp解法是O(n^2),这里用不上。我这里用的是nlogn的。可以声明一个数组dp【1.....n】,dp【i】的意义是什么?它记录的是在整个序列中,长度为 i 的上升子序列末尾元素的最小值。所有如果你当前输入的元素num大于dp【i】,那么你就可以确定当前长度为i+1的上升子序列的末尾元素为num,否则你就要找出dp【1....i】中第一个大于等于num的元素的位置 j,用num来替换dp【j】,这意味着长度为 j 的上升子序列的末尾元素被减小为num了。
而求第一个大于等于num的数的方法可以用二分,因为你在更新dp数组的时候,前面的那些数就是有序的了。当然更简单的可以用《algorithm》中的lower_bound函数,它返回的是有序数组中第一个大于或等于某个数的元素的地址。当所有的元素被输入完后,dp数组下标就是最长上升子序列元素的个数。当然这种方法只能求最长上升子序列元素的个数,无法求出每一个元素是什么。
这一篇博客详细模拟了上面这种方法的每一步,可以看着理解一下:https://blog.csdn.net/fire_to_cheat_/article/details/78297534
代码:
1 #include<iostream> 2 #include<cstdlib> 3 #include<cstring> 4 #include<cstdio> 5 #include<string> 6 #include<cmath> 7 #include<algorithm> 8 #include<stack> 9 #include<queue> 10 #define ll long long 11 #define inf 0x3f3f3f3f 12 #define pi 3.141592653589793238462643383279 13 using namespace std; 14 int main() 15 { 16 int n,num,dp[50005]; 17 while(cin>>n) 18 { 19 scanf("%d",&num); 20 dp[0] = num; 21 int cnt = 1; 22 for(int i=1; i<n; ++i) 23 { 24 scanf("%d",&num); 25 if(dp[cnt-1] < num) dp[cnt++] = num; 26 else //lower_bound函数求第一个大于等于num的数 27 { 28 int p = lower_bound(dp,dp + cnt - 1,num) - dp; //数的地址减去基地址等于数组下标 29 dp[p] = num; 30 } 31 /*else // 二分法求第一个大于等于num的数 32 { 33 int low = 0,high = cnt-1; 34 while(low <= high) 35 { 36 int mid = (low + high)/2; 37 if(dp[mid] < num) 38 low = mid + 1; 39 else if(dp[mid] > num) 40 high = mid - 1; 41 else 42 { 43 low = mid; 44 break; 45 } 46 } 47 dp[low] = num; 48 }*/ 49 } 50 cout<<cnt<<endl; 51 } 52 }