[Jobdu] 题目1500:出操队形
- 题目描述:
-
在读高中的时候,每天早上学校都要组织全校的师生进行跑步来锻炼身体,每当出操令吹响时,大家就开始往楼下跑了,然后身高矮的排在队伍的前面,身高较高的就要排在队尾。突然,有一天出操负责人想了一个主意,想要变换一下队形,就是当大家都从楼上跑下来后,所有的学生都随机地占在一排,然后出操负责人从队伍中抽取出一部分学生,使得队伍中剩余的学生的身高从前往后看,是一个先升高后下降的“山峰”形状。据说这样的形状能够给大家带来好运,祝愿大家在学习的道路上勇攀高峰。(注,山峰只有一边也符合条件,如1,1、2,2、1均符合条件)
- 输入:
-
输入可能包含多个测试样例。
对于每个测试案例,输入的第一行是一个整数n(1<=n<=1000000):代表将要输入的学生个数。
输入的第二行包括n个整数:代表学生的身高(cm)(身高为不高于200的正整数)。
- 输出:
-
对应每个测试案例,输出需要抽出的最少学生人数。
- 样例输入:
-
6 100 154 167 159 132 105 5 152 152 152 152 152
- 样例输出:
-
0 4
最长递增子序列问题的变型,从左找一遍,从右找一遍,再扫描一遍,找出dp1[i] + dp2[i]的最大值。最长递增子序列可以用二分查找优化成O(nlogn)的复杂度。手写了个二分找lower_bound的函数,也可以用STL里的。
1 #include <iostream> 2 #include <cstdio> 3 #include <vector> 4 #include <algorithm> 5 using namespace std; 6 7 int N; 8 vector<int> v; 9 vector<int> dp1, dp2; 10 vector<int> st; 11 12 void binSearch(int target) { 13 if (st.empty() || target > st.back()) { 14 st.push_back(target); 15 } else { 16 int l = 0, r = (int)st.size() - 1, m; 17 while (l <= r) { 18 m = l + ((r - l) >> 1); 19 if (st[m] >= target) r = m - 1; 20 else l = m + 1; 21 } 22 st[l] = target; 23 } 24 } 25 26 void getDP() { 27 st.clear(); 28 for (int i = 0; i < v.size(); ++i) { 29 binSearch(v[i]); 30 dp1[i] = st.size(); 31 } 32 st.clear(); 33 for (int i = (int)v.size() - 1; i >= 0; --i) { 34 binSearch(v[i]); 35 dp2[i] = st.size(); 36 } 37 } 38 39 void solve() { 40 getDP(); 41 int res = 0; 42 for (int i = 0; i < N; ++i) { 43 res = max(res, dp1[i] + dp2[i] - 1); 44 } 45 cout << N - res << endl; 46 } 47 48 int main() { 49 while (scanf("%d", &N) != EOF) { 50 v.resize(N); 51 dp1.resize(N); 52 dp2.resize(N); 53 for (int i = 0; i < N; ++i) scanf("%d", &v[i]); 54 solve(); 55 } 56 return 0; 57 } 58 /************************************************************** 59 Problem: 1500 60 User: hupo250 61 Language: C++ 62 Result: Accepted 63 Time:1050 ms 64 Memory:13244 kb 65 ****************************************************************/