LIS的nlogn算法的两种实现

对于hdu 1950求LIS的问题,若使用常规的n^2算法会超时,所以需要用到LIS的nlogn算法求解。

一、

定义d[k]:长度为k的LIS的最小末尾元素,易知d中元素单调递增。

首先len=1,d[1]=a[1]。

对于a[i],若a[i]>d[len],则可将a[i]拼接在d[len]后成为更长的LIS,所以d[++len]=a[i];

否则,我们要从d[1]到d[len-1]中找到一个j,满足d[j-1]<a[i]<d[j],因为d是单调递增的,并且我们只需要替换d中某一个元素,显然此过程可以使用二分查找以降低时间复杂度为logn,则n个元素的复杂度为nlogn。

hdu1950:

#include<bits/stdc++.h>
#define MAXN 40005
using namespace std;
int d[MAXN],a[MAXN],len;
int binary_search(int i){  
    int left,right,mid;  
    left=0,right=len;  
    while(left<right){  
        mid = left+(right-left)/2;  
        if(d[mid]>=a[i]) right=mid;  
        else left=mid+1;  
    }  
    return left;  
}  
int main()
{
	int T,n,i,j,k;
	scanf("%d",&T);
	while(T--){
		scanf("%d",&n);
		for(i=1; i<=n; ++i) scanf("%d",&a[i]);
		d[1]=a[1];
		len=1;
		for(i=2; i<=n; ++i){
			if(a[i]>d[len]) d[++len]=a[i];
			else{
				int pos=binary_search(a[i]);
				d[pos]=a[i];
			}
		}
		printf("%d\n",len);
	}
	return 0;
}

二、

开一个数组,每次取数组末尾元素back和读到的元素temp做比较,如果temp>back则将back插入到数组末尾;

如果temp< back则二分查找数组中的比temp大的第1个数,并用temp替换它。这也是很好理解的,因为替换一个比temp大的数使得数组的''潜力''增大了——更容易找到更长的LIS。最长序列长度即为数组长度。

hdu1950:

#include<bits/stdc++.h>
using namespace std;
int main()
{
    int n,t,i;
    int a[40005];
    vector<int> v;
    vector<int>::iterator it;
    cin>>t; 
    while(t--){
        cin>>n;
        for(i=0;i<n;i++){
            cin>>a[i];
        }
        v.push_back(a[0]);
        for(i=1;i<n;i++){
            if(a[i]>v.back()){
                v.push_back(a[i]);
            }
            else{
                it=lower_bound(v.begin(),v.end(),a[i]);
                *it=a[i];
            }
        }
        cout<<v.size()<<endl;
        for(i=0;i<v.size();i++) cout<<v[i]<<' ';
        v.clear();
    }
    return 0;
}


猜你喜欢

转载自blog.csdn.net/qq_37016724/article/details/80562545