题目描述
游游得到了一个有
n
n
n 个数字的数列。
游游定义了“平滑值”的概念:平滑值指任意两个相邻的数的差的绝对值的最大值。例如
[1, 2, 5, 7, 8]的平滑值是3。
游游现在想知道,在只修改一个位置的数字(可以修改为任意值) 或者不修改的情况下,数列的平滑值最小是多少?
输入描述:
第一行包含一个数字
n
n
n 代表数列的数字个数。
第二行包含
n
n
n 个数字,代表数列 a。
2
≤
n
≤
1
0
5
2\le n\le 10^5
2≤n≤105
−
1
0
9
≤
a
i
≤
1
0
9
-10^9\le a_i\le 10^9
−109≤ai≤109
输出描述
输出一个整数,代表数列最小的平滑值。
示例1
输入:
3 1 3 4
输出:
1
说明:
将第一个数字修改为 3 3 3,平滑值变为 1 1 1,可以证明这是最优的方案之一。
示例2
输入:
5 -1 1 2 5 7
输出:
2
说明:
将第三个数字修改为 3 3 3,平滑值变为 2 2 2,可以证明这是最优的方案。
分析 1
- 首先,我们肯定要改最大的那一对,假设为
a[i]
和a[j]
,且a[i] > a[j]
,那就有两种改法: -
- 大的慢慢变小
-
- 小的慢慢变大
(不可一蹴而就,一点一点改)
- 每改完一次a[i], 和a[j],次大值和最大值都有可能改变:假设次大值为
a[n]
和a[m]
,i, j 和 n, m可能有重合部分,次大值可能随最大值变大而变小,也可能一起变大,反之亦然。 如果最大、次大都变成0,那就得看次次大值,难道要记录前三大的值吗?这就太麻烦了,只能贪心。
结论:这个思路行不通❌❌❌。
分析 2
直接找最大的,设 ∣ a [ i ] − a [ i − 1 ] ∣ | \ \ a[i] - a[i-1] \ \ | ∣ a[i]−a[i−1] ∣ 为最大:
- i = n - 1,即这两个值在最右边,就令
a[i] = a[i]
,然后再求一遍最小值。 - i - 1 = 0,即这两个值在最左边,就令
a[i - 1] = a[i]
,然后再求一遍最小值。 - 若在中间,则令
a[i - 1] = (a[i - 2] + a[i]) / 2
,求一次最小值,复原数组。a[i] = (a[i - 1] + a[i + 1]) / 2
,再求一遍最小值。即,左右两个值,各变成自身左右相邻值的和的一半。 相当于,把数列的凹凸给磨平。
代码
#include <iostream>
#include <iterator>
#include <vector>
using namespace std;
void find_max(vector<int> a, int &max, int &index) { // 就是个找最大值的
max = index = -1;
int tmp;
for (int i = 1; i < a.size(); i++) {
tmp = a[i] - a[i - 1];
if (tmp > max) {
max = tmp;
index = i;
}
}
}
int main() {
int n;
cin >> n;
vector<int> a(n);
for (int i = 0; i < n; i++) cin >> a[i];
int ans, max, index;
find_max(a, max, index); // 找到最大值,和第二个元素的index
ans = max;
if (index == n - 1) {
a[index] = a[index - 1];
find_max(a, max, index);
ans = max < ans ? max : ans;
}
if (index - 1 == 0) {
a[index - 1] = a[index];
find_max(a, max, index);
ans = max < ans ? max : ans;
}
if (index != n - 1 && index - 1 != 0) {
int tmp = a[index - 1];
a[index - 1] = (a[index - 2] + a[index]) / 2;
find_max(a, max, index);
ans = max < ans ? max : ans;
a[index - 1] = tmp; // 复原
a[index] = (a[index - 1] + a[index + 1]) / 2;
find_max(a, max, index);
ans = max < ans ? max : ans;
}
cout << ans;
}