地址 https://www.acwing.com/solution/content/15094/
给出一个长度为 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总得示例代码
#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 ", res); return 0; }