> 文档中心 > 30天学会基础算法 (合唱队形—线性dp) ^7^

30天学会基础算法 (合唱队形—线性dp) ^7^

题目链接

题目描述

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

输入格式

共二行。
第一行是一个整数 n(2≤n≤100),表示同学的总数。
第二行有 n 个整数,用空格分隔,第 i 个整数 ti (130 <= ti <= 230) ​是第 i 位同学的身高(厘米)。

输出格式

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

输入样例

8186 186 150 200 160 130 197 220

输出样例

4

题目分析

枚举中间点 ti,分别求左边的最长上升子序列和右边的最长递减子序列。求右边的最长递减子序列的时候,可以转化成从右到左的最长上升子序列。把两边出队的人数相加就得到了全部的出队人数。

code

#include #include using namespace std;const int MAX = 105;const int INF = 0x3fffffff;int a[MAX];int dp[MAX];int n;int main(){    cin >> n;    for (int i = 1; i <= n; i++) cin >> a[i];    a[0] = 0;     //初始化,空段     a[n + 1] = 0;    int ans = INF;    //枚举中间点    for (int i = 1; i <= n; i++)     { memset(dp, 0, sizeof(dp)); int cnt = 0; //左边为上升序列 for (int j = 1; j <= i; j++)      for (int k = 0; k < j; k++)  if (a[k] < a[j])dp[j] = max(dp[j], dp[k] + 1); cnt += i - dp[i]; //左边出队的人数  //右边为下降序列 dp[i] = 0;  //注意清零  for (int j = n; j >= i; j--)     for (int k = n + 1; k > j; k--)  if (a[k] < a[j])      dp[j] = max(dp[j], dp[k] + 1);  cnt += n - i + 1 - dp[i]; //右边出队的人数  ans = min(ans, cnt); //取最小值     }    cout << ans;    return 0;}
  • 无注释版
#include #include using namespace std;const int MAX = 105;const int INF = 0x3fffffff;int a[MAX];int dp[MAX];int n;int main(){    cin >> n;    for (int i = 1; i <= n; i++) cin >> a[i];    a[0] = 0;     a[n + 1] = 0;    int ans = INF;    for (int i = 1; i <= n; i++)     { memset(dp, 0, sizeof(dp)); int cnt = 0; for (int j = 1; j <= i; j++)      for (int k = 0; k < j; k++)  if (a[k] < a[j])dp[j] = max(dp[j], dp[k] + 1); cnt += i - dp[i]; dp[i] = 0;  for (int j = n; j >= i; j--)     for (int k = n + 1; k > j; k--)  if (a[k] < a[j])      dp[j] = max(dp[j], dp[k] + 1);  cnt += n - i + 1 - dp[i]; ans = min(ans, cnt);     }    cout << ans;    return 0;}