洛谷 P1091 合唱队形

题目描述

N位同学站成一排,音乐老师要请其中的(N-K)位同学出列,使得剩下的K位同学排成合唱队形。
合唱队形是指这样的一种队形:设K位同学从左到右依次编号为1,2…,K,他们的身高分别为T1,T2,…,TK, 则他们的身高满足T1<…Ti+1>…>TK(1<=i<=K)。
你的任务是,已知所有N位同学的身高,计算最少需要几位同学出列,可以使得剩下的同学排成合唱队形。

输入输出格式

输入格式:

输入文件chorus.in的第一行是一个整数N(2<=N<=100),表示同学的总数。第一行有n个整数,用空格分隔,第i个整数Ti(130<=Ti<=230)是第i位同学的身高(厘米)。

输出格式:

输出文件chorus.out包括一行,这一行只包含一个整数,就是最少需要几位同学出列。

输入输出样例

输入样例#1:

8
186 186 150 200 160 130 197 220

输出样例#1:

4
说明
对于50%的数据,保证有n<=20;
对于全部的数据,保证有n<=100。

分析

正反做一遍最长不下降,正确性显然

nlogn 求最长不下降子序列

我们设d[i]表示长度为i的最长不下降子序列最尾元素的最小值,显然d[i]<=d[i+1],d数组符合单调性。因此对于a[i]只需在d数组中二分即可

lower_bound && upper_bound

lower_bound(a+l,a+r+1,key)表示在a数组的l~r范围进行二份查找(注意你需要保证这个数组的单调性,可以加cmp来表达他的单调性,默认单调性是非递减),找到第一个>=key的位置并返回。
而upper_bound则是找到第一个>key的位置返回。
求最长不下降子序列可以利用C++STL中algorithm库里的这两个东西来减少代码量。

fo(i,1,n)
{
        k=upper_bound(d+1,d+n+1,a[i])-d;
        ans=max(ans,k);
        d[k]=min(d[k],a[i]);
}

代码

#include 
#include 
#include 
#include 
#include 
#define N 200

int read()
{
    int x=0,k=1;
    char ch;
    ch=getchar();
    while(ch<'0' || ch>'9')
    {
        if(ch=='-')
            k=-1;
        ch=getchar();
    }
    while(ch>='0' && ch<='9')
    {
        x=x*10+ch-'0';
        ch=getchar();
    }
    return x*k;
}

int p[N],u[N],d[N];
int fu[N],fd[N];

int main()
{
    int n;
    n=read();
    for(int i=1;i<=n;i++)
        p[i]=read();

    fu[1]=p[1];
    int len=1;
    for(int i=2;i<=n;i++)
    {
        if(fu[len]else fu[std::lower_bound(fu+1,fu+len+1,p[i])-fu]=p[i];
            u[i]=len;
    }

    fd[1]=p[n];
    len=1;
    for(int i=n-1;i>=1;i--)
    {
        if(fd[len]else fd[std::lower_bound(fd+1,fd+len+1,p[i])-fd]=p[i];
        d[i]=len;   
    }

    int ans=0x7ffffff;
    for(int i=1;i<=n;i++)
        ans=std::min(ans,n-u[i]-d[i+1]);
    printf("%d\n",ans);
}

你可能感兴趣的:(初学,模板题,dp)