洛谷P3902 递增

这里写图片描述
.
.
.
.
.

分析

定义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;
}
原文地址:https://www.cnblogs.com/YYC-0304/p/9499908.html