最长递增子序列 (Longest Increasing Subsequence, LIS),POJ 2533, POJ 1631

来源:互联网 发布:coreidraw是什么软件 编辑:程序博客网 时间:2024/06/07 03:21

一、问题描述

设L=<a1,a2,…,an>是n个不同的实数的序列,L的递增子序列是这样一个子序列S=<ak1,ak2,…,akm>,其中k1<k2<…<km且ak1<ak2<…<akm。求最大的m值。比如序列(1, 7, 3, 5, 9, 4, 8)的递增子序列包括(1, 7), (3, 4, 8)等等,最长递增子序列为(1, 3, 5, 8),长度为4。

二、解题思路

(1) 把a1,a2,...,an排序,假设得到a'1,a'2,...,a'n,然后求a与a'的最长公共子序列,这样总的时间复杂度为o(nlg(n))+o(n^2)=o(n^2);

(2) 动态规划的思路:O(n^2) (POJ 2533)

//动态规划的思路:// 另设一辅助数组b,定义b[n]表示以a[n]结尾的最长递增子序列的长度,// 则状态转移方程如下:b[k]=max(b[j] && a[j]<a[k],j<k)+1;// 这个状态转移方程解释如下:在a[k]前面找到满足a[j]<a[k]的最大b[j],然后把a[k]接在它的后面,// 可得到a[k]的最长递增子序列的长度,或者a[k]前面没有比它小的a[j],那么这时a[k]自成一序列,长度为1.// 最后整个数列的最长递增子序列即为max(b[k], 0<=k<=n-1);#include<stdio.h>int main(){int i,j,n,max;int a[1000],b[1000];while(scanf("%d",&n)!=EOF){for(i=0;i<n;i++){scanf("%d",&a[i]);}b[0]=1;max=1;for(i=1;i<n;i++){b[i]=1;for(j=0;j<i;j++){if(a[i]>a[j]&&b[j]+1>b[i]) b[i]=b[j]+1;}if(b[i]>max) max=b[i];}printf("%d\n",max);}return 0;}

(3)动态规划的思路:O(nlogn) (POJ 1631)

#include<stdio.h>//二分查找int BSearch(int c[],int len,int k){int low=0,high=len,mid=len/2;while(low<=high){if(k>c[mid])low=mid+1;else if(k<c[mid])high=mid-1;else return mid;mid=(low+high)/2;}return low;}//用一个变量len记录到目前为止所找出来的最长递增序列的长度。//另外准备一个数组c[],用这个数组表示长度为j的递增序列中最后一个元素的值。//在这里长度为j的递增序列不止一个,我们所要保存是那个最小的。//为什么呢?因为最后一个元素越小,那么这个递增序列在往后被延长的机会越大。//初始化c[0] = -1;len = 0;从第一个元素a[1]开始进行二分查找。//当在c数组里面找到一个数比a[i]小,并且他的后面的数大于或等于a[i]则跳出。//将a[i]添加到这个数的后面。输出low。int main(){int i,j,n,max,test;int a[40000],c[40000];scanf("%d",&test);while(test){test--;scanf("%d",&n);for(i=0;i<n;i++){scanf("%d",&a[i]);}max=1;c[0]=-1;c[1]=a[0];for(i=1;i<n;i++){j=BSearch(c,max,a[i]); //c[j-1]<a[i]<=c[j]c[j]=a[i];             // |       |    |        if(j>max)max=j;        //high   a[i]  low     }                      printf("%d\n",max);}return 0;}

 

原创粉丝点击