九度 1500:出操队形(LIS变形)
在读高中的时候,每天早上学校都要组织全校的师生进行跑步来锻炼身体,每当出操令吹响时,大家就开始往楼下跑了,然后身高矮的排在队伍的前面,身高较高的就要排在队尾。突然,有一天出操负责人想了一个主意,想要变换一下队形,就是当大家都从楼上跑下来后,所有的学生都随机地占在一排,然后出操负责人从队伍中抽取出一部分学生,使得队伍中剩余的学生的身高从前往后看,是一个先升高后下降的“山峰”形状。据说这样的形状能够给大家带来好运,祝愿大家在学习的道路上勇攀高峰。(注,山峰只有一边也符合条件,如1,1、2,2、1均符合条件)
思路
1. 这道题我还蛮想总结一下, 因为与之类似的一道题 Candy 当时就把我做崩溃了. 并且, Leetcode 上买卖股票问题和这个也很类似.
2. 这道题实际上比买卖股票还有简单些. 买卖股票从左到右需要 dp 一遍, 从右到左还需要一遍 dp, 并且函数需要各写各的, 因为我们需要求解的是从左向右上升的序列, 而这道题计算的是两个序列, 分别是从左到右递增和从右到左递增. 这样的话先计算从左到右的, 然后 reverse 数组, 再计算一遍又从右到左的即可, 函数只需要写一个.
3. 写完 LIS, 最后就要讨论边界了. 题目说 11, 22, 1 都算有效的, 但第二个案例却返回 4 , 有些纠结.
代码 未通过九度测试
#include <iostream> #include <stdio.h> #include <algorithm> using namespace std; int longest1[100005]; int longest2[100005]; int height[100005]; int record1[100005]; int record2[100005]; int search(int x, int n, int *record) { int low = 0, high = n-1; while(low <= high) { int mid = (low+high)>>1; if(record[mid] == x) { return mid; }else if(record[mid] > x) { high = mid -1; }else{ low = mid + 1; } } return low; } void lt2rt(int n, int *record, int *longest, int first) { int len = 1; record[0] = first; longest[0] = 1; for(int i = 1; i < n; i ++) { if(height[i] > record[len-1]) { record[len] = height[i]; len++; }else{ int pos = search(height[i], len, record); record[pos] = height[i]; } longest[i] = len; } } int main() { int n; while(scanf("%d", &n) != EOF) { for(int i = 0; i < n; i ++) scanf("%d", height+i); lt2rt(n, record1, longest1, height[0]); reverse(height, height+n); lt2rt(n, record2, longest2, height[n-1]); int maxnum = 0; for(int i = 0; i < n-1; i ++) { int left = longest1[i]; int right = longest2[n-i-2]; maxnum = max(maxnum, left+right); } maxnum = max(maxnum, longest2[n-1]); maxnum = max(maxnum, longest1[n-1]); cout << n-maxnum << endl; } return 0; }