洛谷 合唱队形 lis+lcs

209 阅读3分钟

原题: 洛谷 P1091合唱队形

思路:

  1. 本来想的是,写两个函数,lis(int x)求[1,x]的最长上升子序列,一个lcs(int x)求[x,n]的最长下降子序列

    然后for(1-n)中每次求一个lis和lcs然后求一个max,结果只过了5个测试点

    #include<iostream>
    #include<algorithm>
    #include<vector>
    
    using namespace std;
    int n,arr[100],ind;
    vector<int> t;
    
    // [1,x]的最长上升子序列
    int lis(int x){
        t.clear();
        t.push_back(arr[1]);
        for(int i=2;i<=x;i++){
            if(arr[i]>t.back())
                t.push_back(arr[i]);
            else{
                ind = lower_bound(t.begin(),t.end(),arr[i])-t.begin();
                t[ind] = arr[i];
            }
        }
        return t.size();
    }
    
    // [x,n]的最长下降子序列
    int lcs(int x){
        t.clear();
        t.push_back(arr[x]);
        for(int i=x+1;i<=n;i++){
            if(arr[i]<t.back())
                t.push_back(arr[i]);
            else{
                ind = upper_bound(t.begin(),t.end(),arr[i],greater<int>())-t.begin();
                t[ind] = arr[i];
            }
        }
        return t.size();
    }
    
    int main(){
        //freopen("data.in","r",stdin);
        cin>>n;
        for(int i=1;i<=n;i++)
            cin>>arr[i];
        int mn = 0,a,b;
        for(int i=1;i<=n;i++){
            a = lis(i);
            b = lcs(i);
            mn = max(mn,a+b);
        }
        cout<<n+1-mn;
        return 0;
    }
    

    下载下来数据集,和正确的对比了一下中间值,发现问题处在了lcs上,求出来的lcs多多少少会有偏差,思考了一下,可能是upper_bound加上greater惹的祸(未求证),使得求出的不是严格下降子序列,自己尝试写了个cmp来代替greater,结果程序崩溃.....

    inline bool cmp(int a,int b){
        return a<b;
    }
    

    咋办,不服,还想用upper_bound,试了好久后发现

    不服憋着

    下边是大佬Rumia 的解法,

    1. 从前往后求一边lis,并记录下中间值
    2. 从后往前求一遍lcs(还是用Lis的求法),并记录下中间值
    3. for(1-n)找出当lis[i]+lcs[i]的最大值

注水代码

#include<iostream>
#include<algorithm>
#include<vector>

using namespace std;
int n,arr[105],ind,arr2[105],arr3[105];// arr2放lis,arr3放lcs
vector<int> t;

void lis(){
    t.clear();
    t.push_back(arr[1]);
    arr2[1] = 1;
    for(int i=2;i<=n;i++){
        if(arr[i]>t.back())
            t.push_back(arr[i]);
        else{
            ind = lower_bound(t.begin(),t.end(),arr[i])-t.begin();
            t[ind] = arr[i];
        }
        arr2[i] = t.size();
    }
}

void lcs(){
    t.clear();
    t.push_back(arr[n]);
    arr3[n] = 1;
    for(int i=n-1;i>=1;i--){
        if(arr[i]>t.back())
            t.push_back(arr[i]);
        else{
            ind = lower_bound(t.begin(),t.end(),arr[i])-t.begin();
            t[ind] = arr[i];
        }
        arr3[i] = t.size();
    }
}

int main(){
    freopen("data.in","r",stdin);
    cin>>n;
    for(int i=1;i<=n;i++)
        cin>>arr[i];
    lis();
    lcs();
    int mn = 0,a,b;
    for(int i=1;i<=n;i++){
        mn = max(mn,arr2[i]+arr3[i]);
    }
    cout<<n-mn+1;
    return 0;
}

ps:总觉得哪里还能优化,比如用了两个函数,但是函数大部分步骤都是一样的,能简化成一个...算了,懒...:kissing_smiling_eyes:

题面:

题目描述

NN位同学站成一排,音乐老师要请其中的(N-KNK)位同学出列,使得剩下的KK位同学排成合唱队形。

合唱队形是指这样的一种队形:设K位同学从左到右依次编号为1,2,…,K1,2,…,K,他们的身高分别为T_1,T_2,…,T_KT1,T2,…,T**K, 则他们的身高满足T_1<...<T_i>T_{i+1}>…>T_K(1 \le i \le K)T1<...<T**i>T**i+1>…>T**K(1≤iK)。

你的任务是,已知所有NN位同学的身高,计算最少需要几位同学出列,可以使得剩下的同学排成合唱队形。

输入格式

共二行。

第一行是一个整数N(2 \le N \le 100)N(2≤N≤100),表示同学的总数。

第二行有nn个整数,用空格分隔,第ii个整数T_i(130 \le T_i \le 230)T**i(130≤T**i≤230)是第ii位同学的身高(厘米)。

输出格式

一个整数,最少需要几位同学出列。

输入输出样例

输入 #1复制

8
186 186 150 200 160 130 197 220

输出 #1复制

4