题目地址
https://codeforces.com/problemset/problem/1832/C
题目抽象
给一个长度为 n 的数组 a,定义
C
o
n
t
r
a
s
t
V
a
l
u
e
(
C
V
)
=
∑
i
=
1
n
−
1
∣
a
i
−
a
i
+
1
∣
=
∣
a
1
−
a
2
∣
+
∣
a
2
−
a
3
∣
+
.
.
.
∣
a
n
−
1
−
a
n
∣
\begin{array}{clcr} ContrastValue(CV)&= &\sum_{i=1}^{n-1}|a_i-a_{i+1}| \\ &=&|a_1-a_2| + |a_2-a_3|+...|a_{n-1}-a_n| \end{array}
ContrastValue(CV)==∑i=1n−1∣ai−ai+1∣∣a1−a2∣+∣a2−a3∣+...∣an−1−an∣
任务是找 a 的非空子串 b,使得 b 的 CV 等于 a 的 CV
求 b 的最短长度是多少
题目类型
推导
解题思路
先对 case 进行观察
5 4 2 1 0 0 4
的 CV 等于
CV = |5 - 4| + |4 - 2| + |2 - 1| + |1 - 0| + |0 - 0| + |0 - 4|
= 1 + 2 + 1 + 1 + 0 + 4
= 9
他的最短子串应该是 5 0 4
CV = |5 - 0| + |0 - 4|
= 5 + 4
= 9
可以观察到一个规律,5 4 2 1 0
和 5 0
的 CV 是一样的
有一个猜测:连续递增/递减序列的 CV 等于该序列的头尾
证明
我们可以进行推导来验证这个猜测的正确性:
假设 a1 > a2 > ... > an
CV = |a1 - a2| + |a2 - a3| + ... |an-1 - an|
= a1 - a2 + a2 - a3 + ... + an-1 - an
= a1 - an
假设 a1 < a2 < ... < an
CV = |a1 - a2| + |a2 - a3| + ... |an-1 - an|
= -a1 + a2 - a2 + a3 + ... - an-1 + an
= -a1 + an
由此可得推理:连续递增/递减序列的 CV 等于该序列的头尾
所以我们只需要找到连续递增/递减的子串,保留头尾元素,所得到的子序列一定是最短的
既当
a
i
−
a
i
−
1
a_i - a_{i-1}
ai−ai−1 和
a
i
+
1
−
a
i
a_{i+1} - a_i
ai+1−ai 的符号不同时,既代表 ai 是递增或递减的端点,需要保留,计数+1
代码实现时需要注意两点:
- 如果 a 的元素都相同,那么最短子串就是1个元素
- 在计数的时候不要忘记头尾两个元素
代码
#include <bits/stdc++.h>
using namespace std;
int solve(vector<int> &a, int n) {
int inc = 0;
int cnt = 1;
int last = a[0];
for (int i = 1; i < a.size(); i ++) {
if (a[i] - a[i - 1] > 0) {
if (inc == -1) {
cnt++;
}
inc = 1;
} else if (a[i] - a[i - 1] < 0) {
if (inc == 1) {
cnt++;
}
inc = -1;
}
}
return cnt + (inc != 0);
}
int main() {
int T, n;
cin >> T;
while (T --) {
cin >> n;
vector<int> a(n);
for (int i = 0; i < n; i ++) {
cin >> a[i];
}
cout << solve(a, n) << endl;
}
}