目录php
求最长不降低/上升/降低/不上升/子序列的原理都是同样的,只是有无等于号或者大于小于号的关系,懂了一个其他的就懂了,因此我这里只拿不降低子序列来举例子吧。ios
求最长不降低子序列的话,\(i\)位置应该和\(i\)位置以前的位置里面小于等于\(i\)位置上的值且最长不降低子序列最长的那个,因此能够枚举\(i\)位置以前的每个点,而后按照上面说的选取就行了。数组
题面详见spa
#include<iostream> #include<cstdio> using namespace std; const int Max = 201; int n; int a[Max],f[Max],c[Max]; int Ans[Max]; int main() { scanf("%d",&n); for(int i = 1;i <= n;++ i) scanf("%d",&a[i]),f[i] = 1;//输入数据将以每个数结尾的最长不降低子序列标记为1 for(int i = 1;i <= n;++ i) for(int j = 1;j < i;++ j) if(a[i] >= a[j])//知足最长不降低 , 也就是大于等于 if(f[i] < f[j] + 1) f[i] = f[j] + 1,c[i] = j;//记录 int ans = 0,qwq = 1; for(int i = 1;i <= n;++ i) if(f[i] > ans)//找最大的 ans = f[i],qwq = i;//记录最大的所在的下标 printf("max=%d\n",ans); int aaa = ans; while(aaa --)//到这找回去 { Ans[aaa + 1] = a[qwq];//用数组储存 qwq = c[qwq]; } for(int i = 1;i <= ans;++ i)//正着输出 cout<<Ans[i]<<" "; return 0; }
这才是这篇文章的重点!!!code
开一个数组(注意是额外开一个数组,不是存储读入数字串的数组),第一个数理所固然的放在这个数组的第一个位置上面,若是进来的下一个数是符合不降低条件的,那么久放在他的后面,否则就在这个序列里面找到第一个比他大的数替换掉。get
为何要这样呢?
看完上面应该是有一种疑惑,这样替换的话会不会影响后面的呢?答案固然是否认的。
来讲一下理由:
若是当前数组中放了1,3,5这三个数,下一个数是2,若是放在了第一个比他大的数的位置上,那就会放在3上面,这个时候数组中存储的数据就成了1,2,5,而后当下一个数进来的时候是和5比较是否是符合不降低的条件,因此把2替换在3的位置上面并无影响。
具体说一下:就是若是这个数替换的那个数以后还有数,必然不会影响以后一个数拿过来以后的第一项操做,也就是先比较一下放在最后面是否是会符合不降低,那若是后面没有数了,他就是最后一个的话,用这个小的替换了前面的大的,也必然会使结果更优,因此没有问题。it
//最长不降低子序列nlogn Song #include<cstdio> #include<algorithm> using namespace std; int a[40005]; int d[40005]; int main() { int n; scanf("%d",&n); for (int i=1;i<=n;i++) scanf("%d",&a[i]); if (n==0) //0个元素特判一下 { printf("0\n"); return 0; } d[1]=a[1]; //初始化 int len=1; for (int i=2;i<=n;i++) { if (a[i]>=d[len]) d[++len]=a[i]; //若是能够接在len后面就接上,若是是最长上升子序列,这里变成> else //不然就找一个最该替换的替换掉 { int j=upper_bound(d+1,d+len+1,a[i])-d; //找到第一个大于它的d的下标,若是是最长上升子序列,这里变成lower_bound d[j]=a[i]; } } printf("%d\n",len); return 0; }