POJ 1631 Bridging signals DP(最长上升子序列)

最近一直在做《挑战程序设计竞赛》的练习题,感觉好多经典的题,都值得记录。

题意:给你t组数据,每组数组有n个数字,求每组的最长上升子序列的长度。

思路:由于n最大为40000,所以n*n的复杂度不够了,会超时。

   书上状态方程换成了d[i]——以长度为i+1的上升子序列中末尾元素的最小值。

   那么我们在遍历第i个元素时候,以这个元素为末尾元素的最长子序列也就是在d[i]中找到一个小于num[i]的最大值,然后在这个序列末尾加上num[i]

   显然,我们在查找时便可以利用二分搜索,从而把复杂度从原来的n变为了logn,总复杂度从n*n变成了nlogn

   

   d[i]已经保证了长度为i+1的上升子序列末尾元素的最小值,那么对于d[i+1]长度为i+2的子序列里面,要获得最长,自然就要从长度为i+1的子序列中,挑选末尾元素为最小的子序列后面添加元素。所以d[i+1] > d[i],d数组是一个递增的数组,所以就能用二分搜索了。

  lower_bound(d,d+n,num[i]); //默认数组d为上升数组,返回第一个大于等于num[i]的指针。
  lower_bound(d,d+n,num[i],greater<int>()); //表达数组d为下降数组,返回第一个小于等于num[i]的指针。

AC代码:

#include <cstdio>
#include <algorithm>
#include <iostream>
using namespace std;
const int N = 40005;
const int INF = 0X3F3F3F3F;
int n,t,num[N],d[N];
//d[i] = 长度为i+1的上升子序列中末尾元素的最小值,不存在则INF 
void solve()
{
	for(int i = 0; i < n; i++)
		d[i] = INF;
	for(int i = 0; i < n; i++)
	{
		scanf("%d", num+i);
		*lower_bound(d,d+n,num[i]) = num[i];
	}
	printf("%d
", lower_bound(d,d+n,INF) - d);
}
int main()
{
	scanf("%d", &t);
	while(t--)
	{
		scanf("%d", &n);
		solve();
	}
	return 0;
}

  

原文地址:https://www.cnblogs.com/sevenun/p/5457290.html