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;}