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

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

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

#include <iostream>
#include<algorithm>
#include<vector> 
#include<fstream>
#include<iomanip>
#include<map>
#include<sstream>
#include<cmath> 
#include<stack>
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;i<size;i++){
	 	d[i]=1;
	 	pre[i]=i;//如果没有前驱节点,则前驱节点为自身 
	 	for(int j=0;j<i;j++){
	 		if(a[i]>a[j] &&(d[j]+1)>d[i]){ 
			 d[i] = d[j]+1;
			 pre[i] = j;
	     	}
		}
		if(d[i]>len){
		len = d[i];
		maxk=i; 
	 }
     }
	 //以上结束核心算法,以下为输出序列,LIS的结果序列不唯一,按此方法只能找到结尾数最小的序列 
	 stack<int>st;	
	 int j = maxk,i=maxk;;
	 do{
	 	j=i;
	 	st.push(a[i]);
	 	i = pre[i];
	 } while(i!=j);
	 
	 while(!st.empty()){
	 	cout<<st.top()<<" ";
	 	st.pop();
    }cout<<endl;
	 return len;
}
int main(){
	
	int size;
	cin>>size;
	int *a = new int[size]();
	for(int i=0;i<size;i++){
		cin>>a[i]; 
	}
	cout<<LIS(a,size);
    return 0;
}
测试用例:
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 <iostream>
#include<algorithm>
#include<vector> 
#include<fstream>
#include<iomanip>
#include<map>
#include<sstream>
#include<cmath> 
#include<stack>
using namespace std;
int LIS(int*a,int size){
	int len = 1;
	vector<int>v;
		//核心算法
	 for(int i=0;i<size;i++){
	 	if(v.size()==0)v.push_back(a[i]);
	 	else{
	 		int p = lower_bound(v.begin(),v.begin() +len,a[i]) - v.begin();//找到第一个大于等于a[i]元素的坐标 
	 		v[p] = a[i];  
            if(p == len)  
            len ++; 
		 }
	}
	 return len;
}
int main(){
	int size;
	cin>>size;
	int *a = new int[size]();
	for(int i=0;i<size;i++){
		cin>>a[i]; 
	}
	cout<<LIS(a,size);
    return 0;
}



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

猜你喜欢

转载自blog.csdn.net/qhlpptdyfn/article/details/80386181