题意

  • 共有n个人,每个人有身高,移除若干人,使得序列为{纯升序||纯降序||先升序后降序},求最少的移除人数

思路

  • 方法1:对于整个序列正着求一个最长上升子序列,反着求一个最长上升子序列,枚举每个位置两个序列和,最大的减一即为序列最长长度。
  • 方法2:维护一个二维dp数组,第二维记录当前元素属于升序列还是降序列,对于每个位置,如果他的值大于前面的某个值,则维护升序列,如果他的值小于,则维护降序列,特别的,维护降序列的时候,要比较作为降序列的开始还是衔接在前一个降序列,取两种情况中的最优解。
  • 初值:对于每一个元素,至少都可以自己作为一个序列,所以所有初值为1。

PS

  • 对于第二种方法,不需要在枚举每一个位置,答案要么是一个从头到尾的升序列,要么是先升后降的序列,通过给a[n+1]赋一个极小值来使得a[n+1]一定是先升后降的最后一个被选上的,那么这种情况的最大值就是

AC代码

#include<bits/stdc++.h>
using namespace std;

int main(){
    ios::sync_with_stdio(false);
    cin.tie(0);cout.tie(0);

    int n;
    cin >> n;
    int a[120]={0};
    for(int i=1;i<=n;i++){
        cin >> a[i];
    }
    // 方法一:前后维护两个最长上升子序列
    int f1[120]={0},f2[120]={0};
    for(int i=1;i<=n;i++){
        f1[i]=1;
        for(int j=1;j<i;j++){
            if(a[i]>a[j]){
                f1[i]=max(f1[i],f1[j]+1);
            }
        }
    }
    for(int i=n;i>=1;i--){
        f2[i]=1;
        for(int j=n;j>i;j--){
            if(a[i]>a[j]){
                f2[i]=max(f2[i],f2[j]+1);
            }
        }
    }
    int ans=0;
    for(int i=1;i<=n;i++){
        ans=max(ans,f1[i]+f2[i]);
    }
    cout << n-(ans-1) << endl;
    //方法二:维护一个二维dp数组
    //第二维记录当前元素属于升序列还是降序列
    int f[120][2]={0};
    a[n+1]=-100;
    for(int i=1;i<=n+1;i++){
        f[i][0]=1;
        f[i][1]=1;
        for(int j=1;j<i;j++){
            if(a[i]>a[j]) f[i][0]=max(f[i][0],f[j][0]+1);
            else if(a[i]<a[j]) f[i][1]=max(f[i][1],max(f[j][0]+1,f[j][1]+1));
        }
    }
    // int ans=0;
    // for(int i=1;i<=n;i++){
    //     ans=max(ans,max(f[i][0],f[i][1]));
    // }
    cout << n-max(f[n+1][1]-1,f[n][0]) << endl;

    return 0;
}