You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

4.2 KiB

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

##AcWing 482. 合唱队形

一、题目描述

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

合唱队形是指这样的一种队形:设 K 位同学从左到右依次编号为 12…K,他们的身高分别为 T_1T_2T_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;
}