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

  

猜你喜欢

转载自www.cnblogs.com/headboy2002/p/9102974.html