.
.
.
.
.
分析
定义f[i]表示以位置i为结尾的LIS长度。边界条件为f[0]=1,状态转移方程为
f[i]=max(f[i],f[j]+1)(j=1…i-1 a[i]>=a[j])
我们观察到,f[i]的值由前面的数字推得,所以我们只要从前往后转移,就可以保证每次使用的数字都是已经确定的值。
考虑两个数a[x]和a[y],若x< y且f[x]==f[y],那么在转移的过程中,选择a[x]更有潜力,可以获得最优的值,所以当f数组的值一样时,应选择最小的数。
按照f[i]==k分类,记录f[i]==k的所有i的最小值,f有两个特点:
(1)f[i]在计算过程中单调不升
(2)f数组是有序的,f[i]<=f[i+1]
根据这些性质,可以方便地求解:
(1)设当前求出的LIS长度为ans(初始值为1),当前元素为a[x]
(2)如果a[x]>f[ans],直接加入f数组的末尾,且ans++;否则在f数组中二分查找,找到第一个比a[x]小的数字f[k],f[k+1]=a[x],这样做保证a[x]<=f[k+1](根据性质1,2)
(3)最后的ans即为答案
.
.
.
.
.
程序:
#include<iostream>
using namespace std;
int a[100000],f[100000],n,ans,tj;
int work(int w,int l,int r)
{
while (l<r)
{
int mid=(l+r)/2;
if (w>=f[mid]) l=mid+1;else r=mid;
}
return l;
}
int main()
{
cin>>n;
for (int i=0;i<n;i++)
cin>>a[i];
f[1]=a[0];
for (int i=1;i<n;i++)
{
if (f[ans]<a[i]) tj=++ans;else tj=work(a[i],1,ans+1);
f[tj]=a[i];
}
cout<<n-ans;
return 0;
}