关于LIS(最长上升子序列)DP

最长上升子序列就是给一个序列,求出其中非递减形式的子序列。

O(n²)算法
设数串的长度为n,L[i]为以第i个数为末尾的最长上升子序列的长度,a[i]为数串的第i个数。
        L[i]的计算方法为:从前i-1个数中找出满足a[j]
动归表达式:

#include 
#include
#include 
#include
#include
#include
#include
#include 
#include
using namespace std;
int LIS(int*a,int size){
	int* d = new int[size](); 
	int*pre = new int[size]();//用来回溯,记录每个点的前驱节点 
	int len = 1;
	int maxk = 0;//记录最长结尾的下标 
		//核心算法
		//  d[i] 表示以a[i]结尾的最长子序列的长度。
       // d[]初始化为1。因为子序列最短也是1。 
	 for(int i=0;ia[j] &&(d[j]+1)>d[i]){ 
			 d[i] = d[j]+1;
			 pre[i] = j;
	     	}
		}
		if(d[i]>len){
		len = d[i];
		maxk=i; 
	 }
     }
	 //以上结束核心算法,以下为输出序列,LIS的结果序列不唯一,按此方法只能找到结尾数最小的序列 
	 stackst;	
	 int j = maxk,i=maxk;;
	 do{
	 	j=i;
	 	st.push(a[i]);
	 	i = pre[i];
	 } while(i!=j);
	 
	 while(!st.empty()){
	 	cout<>size;
	int *a = new int[size]();
	for(int i=0;i>a[i]; 
	}
	cout<
测试用例:
input:
9
2 7 1 5 6 4 3 8 9
out:

2 5 6 8 9
5

O(nlogn)算法

我们再举一个例子:有以下序列A[]=3 1 2 6 4 5 10 7,求LIS长度。

  我们定义一个B[i]来储存可能的排序序列,len为LIS长度。我们依次把A[i]有序地放进B[i]里。(为了方便,i的范围就从1~n表示第i个数)

  A[1]=3,把3放进B[1],此时B[1]=3,此时len=1,最小末尾是3

  A[2]=1,因为1比3小,所以可以把B[1]中的3替换为1,此时B[1]=1,此时len=1,最小末尾是1

  A[3]=2,2大于1,就把2放进B[2]=2,此时B[]={1,2},len=2

  同理,A[4]=6,把6放进B[3]=6,B[]={1,2,6},len=3

  A[5]=4,4在2和6之间,比6小,可以把B[3]替换为4,B[]={1,2,4},len=3

  A[6]=5,B[4]=5,B[]={1,2,4,5},len=4 

  A[7]=10,B[5]=10,B[]={1,2,4,5,10},len=5

  A[8]=7,7在5和10之间,比10小,可以把B[5]替换为7,B[]={1,2,4,5,7},len=5

   最终我们得出LIS长度为5。但是,但是!!这里的1 2 4 5 7很明显并不是正确的最长上升子序列。是的,B序列并不表示最长上升子序列,它只表示相应最长子序列长度的排好序的最小序列。这有什么用呢?我们最后一步7替换10并没有增加最长子序列的长度,而这一步的意义,在于记录最小序列,代表了一种“最可能性”。假如后面还有两个数据8和9,那么B[6]将更新为8,B[7]将更新为9,len就变为7。读者可以自行体会它的作用。

因此用O(nlogn)的算法就不知道怎么输出路径了=_ =

#include 
#include
#include 
#include
#include
#include
#include
#include 
#include
using namespace std;
int LIS(int*a,int size){
	int len = 1;
	vectorv;
		//核心算法
	 for(int i=0;i>size;
	int *a = new int[size]();
	for(int i=0;i>a[i]; 
	}
	cout<



参考博客:http://www.cnblogs.com/GodA/p/5180560.html

你可能感兴趣的:(关于LIS(最长上升子序列)DP)