【Codeforces Round #695 (Div. 2) B】Hills And Valleys
题目链接
翻译
让你统计"山"和“山谷"的总个数,要求这个总个数最少。
你可以修改某个数字为任意整数。
题解
首先对于初始的数组,统计一下山加上山谷的总数\(cnt\)
然后枚举第 \(i\) 个位置,现在要对第 \(i\) 个位置上的数字进行修改了。
最直接的想法就是,改了 \(a[i]\) 之后,哪些位置上的山顶和山谷的值会受到影响呢?
只有 \(i-1,i,i+1\) 这 \(3\) 个位置。
所以,在修改之前先减去这 \(3\) 个位置的影响,改了之后再加上影响用于更新最小值即可。
\(a[i]\) 的改法有 \(5\) 种情况(假设\(a[i-1]\) 和 \(a[i+1]\) 中较小值为 \(xiao\),较大值为 \(da\)
那么 \(a[i]\) 可以改为 xiao-1,xiao,xiao+1,da,da+1
这 \(5\) 种情况,这样就能把 \(a[i]\) 对 \(i-1\) 和 \(i+1\)
的影响都考虑到了。
然后 \(cha\) 的时候看了一下别人代码,好像有只需要改成 \(a[i-1]\) 和 \(a[i+1]\) 两者之一就可以的。。
代码
#include <bits/stdc++.h>
using namespace std;
const int N = 3e5;
int n,hills,valleys;
int a[N+10];
int calHill(int i){
if (i == 1 || i == n){
return 0;
}
if (a[i] > a[i-1] && a[i] > a[i+1]){
return 1;
}
return 0;
}
int calValley(int i){
if (i == 1 || i == n){
return 0;
}
if (a[i-1] > a[i] && a[i] < a[i+1]){
return 1;
}
return 0;
}
void sub(int i){
for (int j = -1;j <= 1; j++){
hills -= calHill(i+j);
valleys -= calValley(i+j);
}
}
void add(int i){
for (int j = -1;j <= 1; j++){
hills += calHill(i+j);
valleys += calValley(i+j);
}
}
int main(){
#ifdef LOCAL_DEFINE
freopen("in.txt", "r", stdin);
#endif
ios::sync_with_stdio(0),cin.tie(0);
int T;
cin >> T;
while (T--){
cin >> n;
hills = 0,valleys = 0;
for (int i = 1;i <= n; i++){
cin >> a[i];
}
for (int i = 2;i <= n-1;i++){
hills += calHill(i);
valleys += calValley(i);
}
int mi = hills + valleys;
for (int i = 2;i <= n-1; i++){
sub(i);
int xiao = a[i-1],da = a[i+1];
if (xiao > da){
swap(xiao,da);
}
int temp = a[i];
a[i] = xiao-1;
if (a[i] < 1){
a[i] = 1;
}
add(i);
mi = min(mi,hills + valleys);
sub(i);
a[i] = xiao+1;
if (a[i] > 1e9){
a[i] = 1e9;
}
add(i);
mi = min(mi,hills + valleys);
sub(i);
a[i] = da+1;
if (a[i] > 1e9){
a[i] = 1e9;
}
add(i);
mi = min(mi,hills + valleys);
sub(i);
a[i] = xiao;
add(i);
mi = min(mi,hills + valleys);
sub(i);
a[i] = da;
add(i);
mi = min(mi,hills + valleys);
sub(i);
a[i] = temp;
add(i);
}
cout << mi << endl;
}
return 0;
}