LIS——最长不下降子序列
输入一个n,以及n个数据,输出最长的不下降的子序列
大意:
有两种思路:
1.O(n^2)想法
用一个num来记录以该数字为末数字的长度
用一个if限制两个条件,后面小于前面并且后面的长度大于前面的长度+1那么说明需要更新了,最后只要遍历所有的数字在num中看以那个数字为开始点的长度最长。这个比较好理解
#include<cstdio> const int MAX = 150; int a[MAX],num[MAX]; int main() { int n; scanf("%d",&n); for(int i = 1; i<= n ;i++) scanf("%d",&a[i]); for(int i = 1; i <= n ; i++){ num[i] = 1; for(int j = 1; j < i ; j++){ if(a[i] > a[j] &&num[i] < num[j] + 1) num[i] = num[j]+1; } } int max = 0; for(int i = 1; i <= n ; i++){ if(max < num[i]) max = num[i]; } printf("%d",max); return 0; }
2.O(n*logn)想法
这个比较难以理解
用b[k]来记录以该下标为长度,且最后一个数为它的数值
b[1] = a[1],然后循环,判断是否a[i] > b[k],如果是的话,b长度就变长,值就变成a[i],否则的话从长度为1开始找一直找到以长度为k的,因为b是单调递增的,所以在其中找大于b[j],小于b[j+1]这个值的下标,就相当于在一个单调的串中找一个与要找的n相同的值,用二分法,不过以下标为二分的话,l = mid + 1,r = mid -1 ,返回l的值,左闭右开。
#include<cstdio> using namespace std; const int MAX = 1010; int a[MAX],b[MAX]; int main() { int n,k,l,r,mid; scanf("%d",&n); for(int i = 1; i <= n ; i++) scanf("%d", &a[i]); b[1] = a[1]; int i; for(i = 2, k = 1;i <= n ; i++){ if(a[i] > b[k]) b[++k] = a[i];//b[k]存储长度为k的最后一个数字 else { l = 1, r = k; while(l<= r){//二分查找 mid = (l + r)/2; if(b[mid] < a[i]) l = mid + 1 ; else if(b[mid] > a[i]) r = mid - 1; else break; } b[l] = a[i]; } } printf("%d\n",k); return 0; }
也可以调用库函数 lower_bound 二分函数
lower_bound(a, a + n, k) 用二分找a[i]大于k的最小的指针
int dp[maxn]; void solve() { fill(dp, dp + n ,inf); for(int i = 0 ; i < n ; i++) *low_bound(dp, dp + n ,a[i]) = a[i]; printf("%d\n",lower_bound(dp,dp+n,inf)-dp); }