luogu1970 花匠
题目大意
给出一个序列,要你从中剔除一些元素,使得剩余序列呈一大一小排列。求这排列中元素的最多值。原序列元素个数$n\leq 10^5$。对70%$n$,$n\leq 10^3$。
题解
动态规划
$O(n^2)$方法
定义$f(i,1)$为以$x$为序列终点,且该值比前一个元素的值大的序列长度最大值,$f(i,0)$则是比前一个元素的值小的。则一个递归式为:
$$f(i,1)=1+\max_{1\leq j\leq i-1}f(j,0)$$
$f(i,0)$同理。但是数据范围不允许。
注意
- 如果$a$不小于$b$,则$a=b$是有可能的。
$O(n)$方法
上一个方法的对象是点,所以复杂度高。但是如果我们把针对的对象换作区间可能会降低复杂度。定义$f(i,1)$为在区间$[1,i]$中,结尾元素值大于其紧挨着的元素的值的序列的长度最大值。那么递归式就变成了(设$a_i>a_{i-1}$):
$$f(i,1)=\max\{f(i-1,1),f(i-1.0)+1\}$$
$$f(i,0)=f(i-1,0)$$
#include <cstdio> #include <cstring> #include <algorithm> using namespace std; const int MAX_N = 100010; int A[MAX_N], F[MAX_N][2]; int main() { int n; scanf("%d", &n); for(int i=1; i<=n; i++) scanf("%d", A + i); F[1][0] = F[1][1] = 1; for(int i=2; i<=n; i++) { if(A[i] > A[i-1]) { F[i][1] = max(F[i-1][1], F[i-1][0] + 1); F[i][0] = F[i-1][0]; } else if(A[i] < A[i-1]) { F[i][0] = max(F[i-1][0], F[i-1][1] + 1); F[i][1] = F[i-1][1]; } else { F[i][0] = F[i-1][0]; F[i][1] = F[i-1][1]; } } printf("%d\n", max(F[n][0], F[n][1])); }
贪心
对于任意一个波动序列,如果一个点为波动序列中的波谷,那么我们可以把这个点滑动到原序列的波谷;波峰同理。可以证明,不会有两个点滑到同一个波谷或波峰中。因此,我们选择波峰波谷为波动序列即可。
#include <cstdio> #include <cstring> using namespace std; int Judge(int x, int y) { return x < y ? 1 : x > y ? -1 : 0; } int main() { int n, prev = -1, cur, ans = 0, k = 0; scanf("%d", &n); scanf("%d", &prev); for(int i=2; i<=n; i++) { scanf("%d", &cur); int tk = Judge(prev, cur); if(tk == 0) continue; if(tk != k) { ans++; k = tk; } prev = cur; } ans++; printf("%d\n", ans); return 0; }