最长上升子序列变形,满足a[j]<=a[i],并且再求一个最长下降子序列。
第一种方法:用二分查找
#include <iostream>
#include <string>
#include <algorithm>
#include <cstdio>
#include <cstring>
using namespace std;
const int maxn=3*1e4+5;
int a[maxn];
int sheng[maxn];
int jiang[maxn];
int n;
int lens;
int lenj;
int s(int x)
{
int l=0,r=lens;
while(l<=r)
{
int mid=(l+r)/2;
//cout << "S" << ":" << l << ":" << r << ":" << mid << endl;
if(x>=sheng[mid]) l=mid+1;
else r=mid-1;
}
//cout << endl << l << endl;
return l;
}
int j(int x)
{
int l=0,r=lenj;
while(l<=r)
{
int mid=(l+r)/2;
//cout << "J" << ":" << l << ":" << r << ":" << mid << endl;
if(x<=jiang[mid]) l=mid+1;
else r=mid-1;
}
//cout << endl << l << endl;
return l;
}
int main()
{
cin >> n;
for(int i=0;i<n;i++)
{
cin >> a[i];
}
lenj=0;
lens=0;
memset(jiang,0,sizeof(jiang));
memset(sheng,0,sizeof(sheng));
for(int i=0;i<n;i++)
{
int k=s(a[i]);
//cout << endl;
sheng[k]=a[i];
lens=max(lens,k);
}
for(int i=0;i<n;i++)
{
int k=j(a[i]);
jiang[k]=a[i];
//cout << endl;
lenj=max(lenj,k);
}
lenj++;
cout << n-max(lenj,lens) << endl;
// for(int i=1;i<=lens;i++)
// {
// cout << "**" << sheng[i] << endl;
// }
// cout << endl;
// for(int i=0;i<=lens;i++)
// {
// cout << "**" << jiang[i] << endl;
// }
return 0;
}
二分重新构造序列,记录序列长度即可,注意二分的序列是新序列,不是输入的原序列,而且上升序列从s[1]开始,而下降序列从s[0]开始。
第二种方法是用lower_bound,upper_bound代替二分:
#include <iostream>
#include <string>
#include <algorithm>
#include <cstdio>
#include <cstring>
using namespace std;
const int maxn=1e6;
const int INF=4;
int arr[maxn];
int s[maxn];
int j[maxn];
int main()
{
int n;
while(cin >> n)
{
for(int i=0;i<n;i++)
{
cin >> arr[i];
}
fill(s,s+n,INF);
fill(j,j+n,INF);
for(int i=0;i<n;i++)
{
*upper_bound(s,s+n,arr[i])=arr[i];
}
for(int i=n-1;i>=0;i--)
{
*upper_bound(j,j+n,arr[i])=arr[i];
}
// for(int i=0;i<n;i++)
// cout << s[i];
// cout << endl;
// for(int i=0;i<n;i++)
// cout << j[i];
// cout << endl;
cout << n-max(lower_bound(s,s+n,INF)-s,lower_bound(j,j+n,INF)-j) << endl;
}
return 0;
}
这种方法看似是重构一个新的序列,但是由于upper_bound是返回第一个大于查找值的数的位置,所以下降序列只是长度是正确的。