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;
}
************************************************************************可供吐槽
2188 最长上升子序列
最新推荐文章于 2020-09-12 10:39:40 发布