题目描述
给出一个长度为 n 的由正整数构成的序列,你需要从中删除一个正整数,很显然你有很多种删除方式,你需要对删除这个正整数以后的序列求其最长上升子串,请问在所有删除方案中,最长的上升子串长度是多少。
这里给出最长上升子串的定义:即对于序列中连续的若干个正整数,满足 ai+1>ai,则称这连续的若干个整数构成的子串为上升子串,在所有的上升子串中,长度最长的称为最长上升子串。
输入格式
输入第一行仅包含一个正整数 n,表示给出的序列的长度。
接下来一行有 n 个正整数,即这个序列,中间用空格隔开。
输出格式
输出仅包含一个正整数,即删除一个数字之后的最长上升子串长度。
数据范围
1≤n≤105,
1≤ai≤105
输入样例:
5
2 1 3 2 5
输出样例:
3
算法1
首先考虑使用暴力遍历解答,逐个删除某个数字然后双指针统计上升子串的长度。
过程中我们会发现,如果删除该元素 对于上升子串没有提升的长度 则可以考虑剪枝。
对于上升子串没有提升的长度的定义呢 就是该数字的左边的数小于该数字右边的数。
那么进一步的优化,如果我们首先统计以该数字为结尾和以该数字为开始 的上升子串的长度,
可以减少不少的计算量。
类似如此
a b c d 如果我们实现统计了 以各个元素为结尾的最长上升子串长度 ai bi ci di
统计了 以各个元素为起始的最长上升子串长度 aj bj cj dj.
那么如果 a < c 我们可以尝试删除b,得到子串长度就是 ai+bj;
但如果b < d 我们可以尝试删除c,得到的子串长度就是bi+dj;
代码实现见Y总得示例代码
C++ 代码
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
const int N = 100010;
int n;
int a[N], f[N], g[N];
int main()
{
scanf("%d", &n);
for (int i = 1; i <= n; i ++ ) scanf("%d", &a[i]);
// 预处理f[i]:以i结尾的单调上升子串的最大长度
for (int i = 1; i <= n; i ++ )
if (a[i] > a[i - 1]) f[i] = f[i - 1] + 1;
else f[i] = 1;
// 预处理g[i]:以i开头的单调上升子串的最大长度
for (int i = n; i; i -- )
if (a[i] < a[i + 1]) g[i] = g[i + 1] + 1;
else g[i] = 1;
int res = 0;
// 枚举删除哪个数
for (int i = 1; i <= n; i ++ )
if (a[i - 1] >= a[i + 1]) res = max(res, max(f[i - 1], g[i + 1]));
else res = max(res, f[i - 1] + g[i + 1]);
printf("%d\n", res);
return 0;
}
作者:yxc
链接:https://www.acwing.com/activity/content/code/content/246781/
来源:AcWing
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。