这爬山多是一件美事
题目描述
话说这天老冯爬山给大伙送鸡汤。爬山的过程中,山顶和山谷的连接都会使得山路高低起伏,作为一个催逝员的老冯当然想着让山顶或者山谷如何变少,这走平路多是一件美事啊!于是老冯把这连绵起伏的山脉看作一个长度为
n
n
n的整数数列
a
n
a_n
an。对于任意一个
i
(
1
<
i
<
n
)
i(1<i<n)
i(1<i<n),当
a
i
>
a
i
−
1
a_i>a_{i-1}
ai>ai−1且
a
i
>
a
i
+
1
a_i>a_{i+1}
ai>ai+1时,老冯认为
i
i
i是一个山峰。同理当
a
i
<
a
i
−
1
a_i<a_{i-1}
ai<ai−1且
a
i
<
a
i
+
1
a_i<a_{i+1}
ai<ai+1,老冯认为
i
i
i是个山谷。现在老冯要启动他的山峰改造计划了,假设老冯最多可以修改一次序列
a
n
a_n
an,每次修改操作使得序列
a
n
a_n
an中的一个整数变为另一个整数。那么最后序列中山峰和山谷的数量之和的最小值是多少?
输入
每一行包括一个整数
T
T
T,表示测试数据的组数
每一组测试数据第一行包含一个正整数
n
(
1
≤
n
≤
3
×
1
0
5
)
n(1\le n\le 3\times10^5)
n(1≤n≤3×105),表示山脉长度
每一组测试数据第二行包含
n
n
n个整数
a
1
,
a
2
,
a
3
,
.
.
.
,
a
n
(
0
≤
a
i
≤
1
0
9
)
a_1,a_2,a_3,...,a_n(0\le a_i\le10^9)
a1,a2,a3,...,an(0≤ai≤109),表示山脉序列
保证所有测试数据的
n
n
n之和小于等于
3
×
1
0
5
3\times10^5
3×105
输出
对于每组测试数据,输出包括一行一个整数,表示山峰和山谷数量之和的最小值
样例输入
样例输出
提示
对于第一组测试数据 1 5 3 来说, 将
a
2
a_2
a2修改为1或3即可,此时没有山峰或山谷
对于第二组测试数据 2 2 2 2 2 , 不需要修改
对于第三组测试数据, 一种修改方式是将
a
3
a_3
a3修改为6
AC代码
#include<bits/stdc++.h>
using namespace std;
const int maxn = 3e5 + 10;
int T, n;
int num[maxn];
int ans;
bool check(int x) {
if(x == 1 || x == n) return false;
else if((num[x+1] > num[x] && num[x-1] > num[x]) //山谷
|| (num[x+1] < num[x] && num[x-1] < num[x])) //山峰
return true;
else return false;
}
int main() {
scanf("%d", &T);
while(T--) {
scanf("%d", &n);
for(int i = 1; i <= n; i++) {
scanf("%d", &num[i]);
}
int sum = 0, max_num = 0;
for(int i = 1; i <= n; i++) {
if(check(i)) sum++;
}
ans = sum;
for(int i = 2; i < n; i++) {
int pos = num[i];
int ans1 = check(i) + check(i-1) + check(i+1);
num[i] = num[i-1];
int ans2 = check(i) + check(i-1) + check(i+1);
num[i] = num[i+1];
int ans3 = check(i) + check(i-1) + check(i+1);
num[i] = pos;
max_num = max(max_num, max(ans1-ans2, ans1-ans3));
}
printf("%d\n", max(ans-max_num, 0));
}
return 0;
}
代码分析
假设下标是
i
i
i一个山峰或山谷, 则只需将下表
i
i
i的值尽可能修改为其左边或右边的值或者不进行修改。由于每次修改只可能影响三个位置的状态,只需判断相邻及其位置剩余的峰谷的数量,最后求最小值。
max_num
其实是修改后相对于修改前峰谷的削减量,算出削减量的最大值再用总峰谷数减去最大值便可以得到最小值。