使用二分查找求解最长上升子序列

题目链接:HLOJ 1563 气球排序。

#include<iostream>
#include<vector>
#include<cstdio> //ZOJ 用scanf,printf时必须包含此头文件
using namespace std;

const int N=20000;

int v[N];//存放输入的数值
int a[N];//辅助数组,存放左边小于中间数的数,右边大于中间数的数
int f[N];//辅助数组,存放LOS长度

//状态:f[i],以第i个元素(v[i])为最后一个元素的最长上升子序列的长度
//方程:f[i]=max(f[j]+1, f[i]), 0<=j<i && v[i]>v[j]
//初值:f[i]=1,0<=i<n,考虑只有v[i]一个数时的上升序列的长度
//结果:f[]中的最大值

int BSearch(vector <int> a, int t) //二分查找
{
	int n=a.size(), low=0, high=n-1;
	
	while (low<=high)
	{
		int mid=(low + high)/2;
		if (t==a[mid]) return mid;
		else if (t>a[mid]) low=mid+1;
		else high=mid-1;
	}
	return low;
}

int LosBS(int a[], int n) //使用二分查找的最长上升子序列
{
	vector <int> tv; //存放构成最长上升子序列的数据
	tv.push_back(a[0]);
	for (int i=1; i<n; i++)
	{
		int k=tv.size()-1;

		if (a[i]>tv[k]) //当前考虑的数(a[i])大于当前最长上升子序列(tv)的最后一个数则直接放到最后
		{
			tv.push_back(a[i]);
		}
		else //在当前最长上升子序列中使用二分查找求得存放当前考虑的数的位置并放入该数
		{
			int j=BSearch(tv, a[i]);
			tv[j]=a[i];
		}
	}
	return tv.size();
}

void run()
{
    int i, n, k=0;
    scanf("%d", &n); 
	
	fill(f, f+n, 0);

	for(i=0;i<n;i++) scanf("%d", &v[i]);

	int m=(n-1)/2;	
	
	for (i=0;i<m;i++) 
	{
		if (v[i]<v[m])
			a[k++]=v[i];
	}

	a[k++]=v[m];
	int l1=LosBS(a,k);

	k=0;
	for (i=m+1;i<n;i++) 
	{
		if (v[i]>v[m])
			a[k++]=v[i];
	}
	
	int l2=LosBS(a,k);

	if (k==0) l2=0;
 
    printf("%d\n", n-l1-l2);
}

int main()
{
#ifndef ONLINE_JUDGE
    freopen("e:\\1.txt", "r", stdin);	
#endif
	int T;

	scanf("%d", &T);
	for(int i=1; i<=T; i++) run();
	
	return 0;
}


你可能感兴趣的:(二分查找,最长上升子序列)