AcWing 482. 合唱队形

\(AcWing\) \(482\). 合唱队形

一、题目描述

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

合唱队形是指这样的一种队形:设 \(K\) 位同学从左到右依次编号为 \(1,2…,K\),他们的身高分别为 \(T_1,T_2,…,T_K\),  则他们的身高满足 \(T_1<…<T_i>T_i+1>…>T_K(1≤i≤K)\)

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

输入格式

输入的第一行是一个整数 \(N\),表示同学的总数。

第二行有 \(N\)个整数,用空格分隔,第 \(i\) 个整数 \(T_i\) 是第 \(i\)位同学的身高(厘米)。

输出格式

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

数据范围

\(2≤N≤100,130≤T_i≤230\)

输入样例

8
186 186 150 200 160 130 197 220

输出样例

4

二、题意分析

就是和上一道的登山一样一样的,差异是:,它问的是 需要出列几个同学

\(Code\)

#include <bits/stdc++.h>

using namespace std;

const int N = 110;
int n;    // 同学的个数
int a[N]; // 山的高度数组

int f[N]; // 从左向右的最长子序列
int g[N]; // 从右向左的最长子序列
int res;

int main() {
    cin >> n;
    for (int i = 1; i <= n; i++) cin >> a[i];

    // 正向求解 LIS问题
    for (int i = 1; i <= n; i++) {
        f[i] = 1;
        for (int j = 1; j < i; j++)
            if (a[i] > a[j]) f[i] = max(f[i], f[j] + 1);
    }
    // 反向求解 LIS问题
    for (int i = n; i >= 1; i--) {
        g[i] = 1;
        for (int j = n; j > i; j--)
            if (a[i] > a[j]) g[i] = max(g[i], g[j] + 1);
    }

    // 因为最终的那个中间点,左边计算了一次,右边双计算了一次,需要减1去重复
    for (int i = 1; i <= n; i++) res = max(res, f[i] + g[i] - 1);
    // 问至少要出队几人,和问“mx=满足要求的队列,最长是多长”是直接相关的,如果算得mx,则n-mx就是答案
    printf("%d\n", n - res);
    return 0;
}

三、贪心+二分+记录路径\(O(NlogN)\)代码

\(Code\)

#include <bits/stdc++.h>

using namespace std;
const int N = 1010;
int n, a[N];
int f[N], fl, p1[N];
int g[N], gl, p2[N];

int res;

int main() {
    cin >> n;
    for (int i = 1; i <= n; i++) cin >> a[i];

    // 正向
    f[++fl] = a[1];
    p1[1] = 1;

    for (int i = 2; i <= n; i++)
        if (a[i] > f[fl]) {
            f[++fl] = a[i];
            p1[i] = fl;
        } else {
            int t = lower_bound(f + 1, f + fl + 1, a[i]) - f;
            f[t] = a[i];
            p1[i] = t;
        }

    // 反向
    g[++gl] = a[n];
    p2[n] = 1;

    for (int i = n - 1; i >= 1; i--)
        if (a[i] > g[gl]) {
            g[++gl] = a[i];
            p2[i] = gl;
        } else {
            int t = lower_bound(g + 1, g + gl + 1, a[i]) - g;
            g[t] = a[i];
            p2[i] = t;
        }

    for (int i = 1; i <= n; i++) res = max(res, p2[i] + p1[i] - 1);

    printf("%d\n", n - res);
    return 0;
}

四、状态机解法

\(Code\)

#include <bits/stdc++.h>

using namespace std;

const int N = 1010;

int n;
int a[N];
int f[N][2];

int main() {
    // input
    cin >> n;
    for (int i = 1; i <= n; i++) cin >> a[i];

    // dp
    for (int i = 1; i <= n; i++) {
        f[i][0] = f[i][1] = 1;
        for (int j = 1; j < i; j++) {
            if (a[j] < a[i]) f[i][0] = max(f[i][0], f[j][0] + 1);
            if (a[j] > a[i]) f[i][1] = max(f[i][1], max(f[j][0], f[j][1]) + 1);
        }
    }

    // find result from all final states
    int res = 0;
    for (int i = 1; i <= n; i++) res = max({res, f[i][0], f[i][1]});
    cout << n - res << endl;
    return 0;
}
posted @ 2021-12-06 09:53  糖豆爸爸  阅读(147)  评论(0编辑  收藏  举报
Live2D