您的位置:首页 > 编程语言 > C语言/C++

动态规划-最长上升子序列(LIS)

2012-11-27 16:48 351 查看
这题目是经典的DP题目,也可叫作LIS(Longest Increasing Subsequence)最长上升子序列 或者 最长不下降子序列。很基础的题目,有两种算法,复杂度分别为O(n*logn)和O(n^2) 。

利用LCS算法实现
思路:设原序列为A[],将A[]进行排序后生成排好序的序列B[],利用LCS 算法查找A[],B[]的最长公共子序列即可找出LIC。
时间复杂度:分为排序和LCS两块,最终为O(n^2)
代码可以参考前面的关于LCS的文章。

动态规划的思想
定义L(j)={ max(L(i))+1, i<j且a[i]<a[j] }。也就是说,我们需要遍历在j之前的所有位置i(从0到j-1),找出满足条件a[i]<a[j]的L(i),求出max(L(i))+1即为L(j)的值。最后,我们遍历所有的L(j)(从0到N-1),找出最大值即为最大递增子序列。时间复杂度为O(N^2)。
代码实现:

#include <iostream> #define SIZE 10000 #include <vector> #include <stdlib.h> using namespace std; //a[]为序列数组,d[t]记录0...t的LIS的最大长度,p[]记录前一个LIS的位置 void LIS(int *a,int length,int *d,int* p) {     int i,j;     d[0]=1;p[0]=-1;             //初始化,以a[0]结尾的最长递增子序列长度为1     for(i=1;i<length;i++)     {         d[i]=1;p[i]=-1;         //d[i]最小值为1         for(j=0;j<i;j++)             if(a[i]>a[j]&&d[j]+1>d[i])             {                 d[i]=d[j]+1;                 p[i]=j;            //记录位置             }     } } void printAnswer(int *d,int* p,int length) {     int i,j,max=0;     for(i=0;i<length;i++)     {         if(d[i]>max)         {             max=d[i];             j=i;         }     }     cout<<"Max length:"<<max<<endl;     while(p[j]!=-1)     {         cout<<p[j]<<"  ";         j=p[j];     } } int main() {     int i,a[SIZE],d[SIZE],p[SIZE],c[SIZE];     vector<int> vec;     for(i=0;i<SIZE;i++)     {         vec.push_back(i);     }     srand(10);     for(i=0;i<SIZE;i++)     {         vec.push_back(i);     }     for(i=0;i<SIZE;i++)     {         int index = rand() % vec.size();     	vector<int>::iterator iter = vec.begin() + index;     	a[i]=*iter;         vec.erase(iter);     }     cout<<"LIS:"<<SIZE<<"numbers!"<<endl;     LIS(a,SIZE,d,p);     printAnswer(d,p,SIZE);     return 0; }

运算结果:



O(nlgn)算法
目的:在前i个元素中的所有长度为len的递增子序列中找到这样一个序列,它的最大元素比arr[i+1]小,而且长度要尽量的长,如此,我们只需记录len长度的递增子序列中最大元素的最小值就能使得将来的递增子序列尽量地长。

方法:维护一个数组MaxV[i],记录长度为i的递增子序列中最大元素的最小值,并对于数组中的每个元素考察其是哪个子序列的最大元素,二分更新MaxV数组,最终i的值便是最长递增子序列的长度。

代码实现:

//在a[0]..a[len]中,找到最大的j,满足a[j] <= n int find(int *a,int len,int n) {     if(len==1) return 1;     int left=0,right=len-1,mid=(left+right)/2;     while(left<=right)     {         if(n>a[mid]) left=mid+1;         else if(n<a[mid]) right=mid-1;         else return mid;         mid=(left+right)/2;     }     return left; } void LIS2(int *a,int length,int *c,int *d,int* p) {     int i,j,len;     c[0]=0;//     c[1]=a[0];//     d[0]=1;     len=0;     for(i=0;i<SIZE;i++)     {         if(a[i]>c[len]) j=++len;         else j=find(c,len,a[i]);         c[j]=a[i];     }     cout<<"MaxLength"<<len<<endl; }
int main() {     int i,a[SIZE],d[SIZE],p[SIZE],c[SIZE];     vector<int> vec;     for(i=0;i<SIZE;i++)     {         vec.push_back(i);     }     srand(10);     for(i=0;i<SIZE;i++)     {         vec.push_back(i);     }     for(i=0;i<SIZE;i++)     {         int index = rand() % vec.size();     	vector<int>::iterator iter = vec.begin() + index;     	a[i]=*iter;         vec.erase(iter);     }     cout<<"LIS:"<<SIZE<<"numbers!"<<endl;     LIS2(a,SIZE,c,d,p);     return 0; }

得到的结果与前面的算法相同,不过只能打印出LIS的大小,还没想到怎样去打印出序列。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  c++