自己在做有关俄罗斯套娃的题目时,发现自己写出的一个方法可以解决求最长上升子序列(LIS)和最长下降子序列(LDS)的问题。
俄罗斯套娃:这个问题在前一篇中讲的有,在此处就不多讲了~链接
求最长上升子序列:
给定排好序的一堆数列中,求其的LIS长度。它的LIS长度就是它非上升子序列的个数。
WHY?
其实自己模拟一下就可以发现:计算出第一组非上升子序列,它的最后一个数一定是这组数列的最小的一个数;第二组非上升子序列的最后一个数就一定会是剩下的数中最小的一个..........哪么,上升子序列的长度是多少,就一定可以排出多少组非上升子序列~但每一组非上升子序列的最后一个数并不一定就是所求上升子序列的里的数,但每一组一定有一个数是所求最长上升子序列里的数.........
PS:(把上面这段话写清楚一些~)
因为给出的数字(a[])是已经排好序的,因此这组数字的最长上升子序列的第一个数(用b[0]表示),b[0]应该在这组数(a[])的第一组非上升子序列里面。
WHY1?
// 这是因为第一组最长非上升子序列是从这组数的第一个数字开始的而且是能搜索到这组数(a[])最小的数字的!!!故最长上升子序列的第一个数b[0],只能在第一组非上升子序列里~
哪么~b[0]确定的话,b[1]是绝对不会在b[0]这组里面,它存在于第二组非上升子序列里~
WHY2?
// 因为排好的是第一组非上升子序列,若b[0]确定是这组的某个数,哪么该数后面的数绝对<=b[0],故b[1]只会在第二组非上升子序列里,至于这个为什么~请参照WHY1(为什么b[0]在第一组非上升子序列里)
同理~剩下b[]就在接下来的非上升子序列里了~~~~~
............................. = =感觉写的还是........不是很清楚啊.........算了~就这样了,懒得再写了~.....= =
那么肿么输出最长上升子序列?
从最后开始记起~另开一个数组b[]记录LIS值。设给定的数组为a[],它的LIS的长度为number,并把数组已经按非上升子序列分好组,并已经作好标记(列如:是第一组非上升子序列,就标记为1;是第2组就标记为2;以此类推.......)。
从数列的最后开始扫描,当a[i]=number时,就记录b[number]=a[i],并且number--;......一直扫到a[0]结束~b[]记录的就是其中一组的最长上升子序列~
例如:9,10,6,7,2,1,8,4,3,5
第一组非上升子序列是:9 6 2 1--------------记为1
第二组非上升子序列是:10 7 4 3-------------记为2
第三组非上升子序列是:8 5-------------------记为3
那么从后面扫到的第一个符合要求的是5,接着是3,再者是1
然后输出的是:1 3 5
1 3 5就是这组数列其中最小的一组最长上升子序列~
在此感谢荆红浅醉小童鞋的帮助~~
代码如下:
1 #include <iostream> 2 #include <cstdio> 3 #include <cstring> 4 using namespace std; 5 6 class P 7 { 8 public: 9 int x,id; 10 }a[1000]; 11 12 int main() 13 { 14 int n,i,j,number,minn,b[1000]; 15 while(~scanf("%d",&n)) 16 { 17 memset(a,0,sizeof(a)); 18 for(i=0;i<n;i++) 19 scanf("%d",&a[i].x); 20 number=0; 21 printf("LIS: "); 22 for(i=0;i<n;i++) 23 if(a[i].id==0) 24 { 25 number++; //有一组新的非上升子序列,个数就加1 26 a[i].id=number; 27 minn=a[i].x; 28 for(j=i+1;j<n;j++) 29 { 30 if(a[j].id==0 && a[j].x<=minn) 31 { 32 a[j].id=number; //是哪一组的非上升子序列就记录下来它的组值~ 33 minn=a[j].x; //更新每一组非上升子序列的最小值 34 } 35 } 36 } 37 for(i=n-1,j=number;i>=0;i--) //从数列的后面开始扫描~ 38 { 39 if(a[i].id==j) //当遇到id为j,就是所要输出的值,要记入数组b[]中,并找下一个要求值,此时j-- 40 { 41 b[j]=a[i].x; 42 j--; 43 } 44 } 45 for(i=1;i<=number;i++) //输出的b[]就是所求的一个最长上升子序列(应该是最小的一个~) 46 printf("%d ",b[i]); 47 printf(" LIS number:"); 48 printf("%d ",number); 49 } 50 return 0; 51 }
那么~求最长下降子序列方法与上面相反~