zoj 1986

我out了,想不到最长上升子序列还有nlogn的算法,看来以前学得不够仔细,于是把代码贴出来学习一下。

        参考:http://blog.youkuaiyun.com/Hashmat/article/details/5883605

http://acm.tzc.edu.cn/acmhome/topicListAction.do?method=topicDetail&id=52a13560222e8d9c012249d36cc10af1

/**
  设A[t]表示序列中的第t个数,F[t]表示从1到t这一段中以t结尾的最长上升子序列的长度,初始时设F [t] = 
0(t = 1, 2, ..., len(A))。则有动态规划方程:F[t] = max{1, F[j] + 1} (j = 1, 2, ..., t - 1, 且A
[j] < A[t])。
	根据F[]的值进行分类。对于F[]的每一个取值k,我们只需要保留满
足F[t] = k的所有A[t]中的最小值。设D[k]记录这个值,即D[k] = min{A[t]} (F[t] = k)。 
注意到D[]的两个特点: 
(1) D[k]的值是在整个计算过程中是单调不上升的。 
(2) D[]的值是有序的,即D[1] < D[2] < D[3] < ... < D[n]。 
需要证明一下
     假设d[2] >= d[3]:
      a. d[2] 在 d[3] 前面,发现d[3]前的一个元素可以代替d[2]
      b. d[2] 在 d[3] 后面,发现d[2]不成立.

  于是,主要的工作就是维护这个单调的队列。
 */

#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;

#define P	40010
#define hpmax(a,b)	((a)>(b)?(a):(b))

int D[P], map[P];
int p;

int find( int len, int value )	//寻找第一个大于或等于value的下标
{
	int i, j, mid;
	i = 1; 
	j = len;
	while ( i <= j ) {
		mid = (i+j)>>1;
		if ( D[mid] > value ) 
			j = mid-1;
		else if ( D[mid] < value )
			i = mid+1;
		else 
			return mid;
	}
	return i;
}

int main()
{
	int t, i, j, k;
	scanf("%d", &t );
	while ( t -- ) {
		scanf("%d", &p);
		for ( i = 1; i <= p; ++ i ) {
			scanf("%d", &map[i] );
		}

		D[1] = map[1];
		j = 1;
		for ( i = 2; i <= p; ++ i ) {
			if ( map[i] > D[j] ) {
				D[++j] = map[i];
			}
			else {
				k = find( j, map[i] );
				D[k] = map[i];
			}
		}
		printf("%d\n", j );
	}
	return 0;
}




评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值