A. DZY Loves Sequences(ranting 1600)超详细题解
前言
本系列的意义在于两点,一则是作为本人(一个编程初学者)的学习笔记记录,二则希望能对后来者提供一些帮助,因为本人也为新手,难免有些错误或讲述不清之处,恳请大家指出或提出建议,本人也会虚心修改。
本系列的目标是帮助大家解决一些codeforces上,ranting1600+(或者之后会改为1300+,看博主水平吧)的题目
一、题目及翻译
1. 原题(贴图)
2. 翻译
- 机翻版
- 省流版
找到一个最长严格递增子序列。但你可以更改一次任意一个字符,来形成该最长严格递增子序列
二、解析及AC代码
1.解析
如果没有可以更改一次的条件的话,本题就是一个基础的板子(dp最长递增子序列,记为 s i s_i si),那么有了这个条件又该怎么办呢?我们可以对一个 a i a_i ai,我们既求以它结尾的最长递增子序列,也求以它开头的最长递增子序列。之后我们再对数组历遍一次,有两种状态转移的可能:
- 对于
a
i
a_i
ai,如果
a
i
−
1
a_{i-1}
ai−1和
a
i
+
1
a_{i+1}
ai+1间差值大于等于2,那么我们就可以把
a
i
a_i
ai改为它俩之间的数,从而合并出一个长度为
s
i
−
1
s_{i-1}
si−1+
s
i
+
1
s_{i+1}
si+1的新的最长子序列
2. 反之,就无法通过更改 a i a_i ai来构造新的最长递增子序列,但仍有可能为最长,所以也要状态转移
那么,此时同学们就可以自己去尝试一下了,如果还是不太理解,可以参考下博主下面的AC代码
2.AC代码
#define _CRT_SECURE_NO_WARNINGS 01
#include <cstring>
#include <iostream>
using namespace std;
typedef long long LL;
const int N = 100010;
LL a[N];
int f[N];//i前的最长递增
int f2[N];//i后的最长递增
int main()
{
int n;
scanf("%d", &n);
for (int i = 1; i <= n; ++i) scanf("%lld", &a[i]);
if (n == 1 || n == 2)
cout << n << endl;
else
{
f[1] = 1;
for (int i = 2; i <= n; ++i)
{
f[i] = 1;
if (a[i] > a[i - 1]) f[i] = f[i - 1] + 1;
}
f2[n] = 1;
for (int i = n - 1; i >= 1; --i)
{
f2[i] = 1;
if (a[i] < a[i + 1]) f2[i] = f2[i + 1] + 1;
}
int res = 0;
//改一个,且还连不上
for (int i = 1; i <= n - 1; ++i)
{
if (a[i] >= a[i + 1]) res = max(res, f[i] + 1);
}
for (int i = n - 1; i >= 1; --i)
{
if (a[i] >= a[i + 1]) res = max(res, f2[i + 1] + 1);
}
//改一个,能把前后连上
for (int i = 2; i <= n - 1; ++i)
{
if (a[i + 1] - a[i - 1] >= 2) res = max(res, f2[i + 1] + f[i - 1] + 1);
}
printf("%d\n", res);
}
return 0;
}
如果觉得有用还请点个赞吧,拜托拜托