##[$AcWing$ $482$. 合唱队形 ](https://www.acwing.com/problem/content/484/) ### 一、题目描述 $N$ 位同学站成一排,音乐老师要请其中的 $(N−K)$ 位同学出列,使得剩下的 $K$ 位同学排成合唱队形。      合唱队形是指这样的一种队形:设 $K$ 位同学从左到右依次编号为 $1,2…,K$,他们的身高分别为 $T_1,T_2,…,T_K$,  则他们的身高满足 $T_1<…T_i+1>…>T_K(1≤i≤K)$。      你的任务是,已知所有 $N$ 位同学的身高,计算最少需要几位同学出列,可以使得剩下的同学排成合唱队形。 **输入格式** 输入的第一行是一个整数 $N$,表示同学的总数。 第二行有 $N$个整数,用空格分隔,第 $i$ 个整数 $T_i$ 是第 $i$位同学的身高(厘米)。 **输出格式** 输出包括一行,这一行只包含一个整数,就是最少需要几位同学出列。 **数据范围** $2≤N≤100,130≤T_i≤230$ **输入样例**: ```cpp {.line-numbers} 8 186 186 150 200 160 130 197 220 ``` **输出样例**: ```cpp {.line-numbers} 4 ``` ### 二、题意分析 就是和上一道的登山一样一样的,差异是:,它问的是 **需要出列几个同学**! #### $Code$ ```cpp {.line-numbers} #include 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$ ```cpp {.line-numbers} #include 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$ ```cpp {.line-numbers} #include 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; } ```