2188 最长上升子序列

2188 最长上升子序列
 时间限制: 1 s
 空间限制: 32000 KB
 题目等级 : 钻石 Diamond

题目描述 Description
LIS问题是最经典的动态规划基础问题之一。如果要求一个满足一定条件的最长上升子序列,你还能解决吗?
给出一个长度为N整数序列,请求出它的包含第K个元素的最长上升子序列。
例如:对于长度为6的序列<2,7,3,4,8,5>,它的最长上升子序列为<2,3,4,5>,
      但如果限制一定要包含第2个元素,那么满足此要求的最长上升子序列就只能是<2,7,8>了。

输入描述 Input Description

第一行为两个整数N,K,如上所述。
接下来是N个整数,描述一个序列。

输出描述 Output Description

请输出两个整数,即包含第K个元素的最长上升子序列长度。

样例输入 Sample Input
8 6
65 158 170 299 300 155 207 389

样例输出 Sample Output
4

数据范围及提示 Data Size & Hint

80%的数据,满足0<n<=1000,0<k<=n
100%的数据,满足0<n<=200000,0<k<=n

******************************数组处理+DP+二分查找 

#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>

using namespace std;

int f[200010],a[200010],b[200010];

int main()
{
	memset(f,0,sizeof(f));
	int i,j,n,k,maxf,ans = 0;
	int count = 1;
	cin>>n>>k;
	for(i = 1; i <= n; i++)
	{
		cin>>a[i];
	}
	
	for(i = 1; i < k; i++)           //形成b,新数组 
	{
		if(a[i] < a[k])
		{
			b[count++] = a[i];
		}
	}
	b[count++] = a[k];
	for(i = k+1; i <= n; i++)
	{
		if(a[i] > a[k])
		{
			b[count++] = a[i];
		}
	}
	count--;                        //形成b,新数组 

	memset(a,0,sizeof(a));
	a[1] = b[1];                     //a[l]记录长度为l的子串末数字最小值,是个递增数组
	f[1] = 1;
	int left,right,len = 1,mid,pos;
	for(i = 2; i <= count; i++)
	{
/*		maxf = 0;
		for(j = i+1; j <= n; j++)        //其实这里实质是 查找 !!可优化!!
		{
			if(b[i] < b[j] && f[j] > maxf)
			{
				maxf = f[j];
			}
		}
		f[i] = maxf + 1;*/
		left = 1 , right = len;
		pos = 0;
		while(left <= right)
		{
			mid = left + (right-left)/2;
			if(a[mid] < b[i])
			{
				pos = mid;
				left = mid+1;
			}
			else
			{
				right = mid-1;
			}
		}
		f[i] = pos + 1;
		if(f[i] > len) len = f[i];
		a[f[i]] = b[i];
		/* 
		   理解: 假设序列为 27 489 1996 423 ,当i = 4时
		          d[2] = 489 , d[1] = 27,则二分查找出来的f[i] = pos + 1 = 1 + 1 = 2;
				  ∵ ↑ 不为0且leni = 2 这个值再现了
				  ∴这个b[4]肯定是比之前的d[f[i]]小的,所以直接a[f[i]] = b[i];
				  依此类托,便可知为什么不需要下面的判断: 
				  		if(a[f[i]])
		                    a[f[i]] = min(a[f[i]],b[i]);
		                else
		                    a[f[i]] = b[i]; 
		*/ 
	}
	cout<<len<<endl;
	return 0;
}
************************************************************************可供吐槽
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值