This problem is possible to solve using Dynamic Programming approach.
We will reduce full task to sub-tasks recursively. For each position i within array if we're allowed to modify k0 elements lets build an array d[i][k0] that says what is the answer for sub-task of array A[0..i] and custum k0 values to change. With one exception - d[i][k0] gives optimal solution for the case if A[i] is not being changed.
Now if we can build an answer for d[i][k0] if we know all answers for smaller i. Remember that by definition A[i] is not changed. It means we can build a segment of changed values A[l+1..i-1] (here segment_len = i - l - 1) plus use answer d[l][k0 - segment_len], out of these two we can see that d[i][k0] is maximum of d[l][k0 - segment_len] and minimized difference inside changed segment. We can see that segment can be built optimally just by taking step equal to (last - first + segment_len - 1) / segment_len.
Now iterating over all lengths of a segments we can find minimal possible d[i][k0]. Final answer for whole task is d[n][k].
I think better optimization is possible, but I solved this Dynamic Programming task through 3 nested loops. If I give random input to my code, it returns answer after exactly 1 second for n = 1000, k = 500 and after 8 seconds for n = 2000, k = 1000.
I even have ideas how to speedup this considerably. But probably I'll implement them later if I have time.
So time complexity of my algorithm is O(n * k^2) of simple operations and memory complexity is O(n * k).
Try it online!
#include <cstdint>
#include <vector>
#include <limits>
using i32 = int32_t;
i32 Solve(std::vector<i32> const & A, i32 n, i32 k) {
std::vector<std::vector<i32>> d(n + 1, std::vector<i32>(k + 1));
for (i32 i = 0; i <= n; ++i)
for (i32 k0 = 0; k0 <= k; ++k0) {
i32 rmin = std::numeric_limits<i32>::max();
for (i32 j = 0; j <= k0; ++j) {
i32 r = 0;
if (i - 1 - j < 0)
r = 0;
else if (i >= n)
r = d[i - 1 - j][k0 - j];
else {
i32 step = (std::abs(A[i] - A[i - 1 - j]) + j) / (j + 1),
prev = d[i - 1 - j][k0 - j];
r = std::max(step, prev);
}
rmin = std::min(rmin, r);
}
d[i][k0] = rmin;
}
return d[n][k];
}
#include <random>
#include <chrono>
#include <iomanip>
#include <iostream>
void Test() {
auto Time = []{
static auto const gtb = std::chrono::high_resolution_clock::now();
return std::chrono::duration_cast<std::chrono::duration<double>>(
std::chrono::high_resolution_clock::now() - gtb).count();
};
std::mt19937_64 rng{std::random_device{}()};
std::uniform_int_distribution<i32> distr(-1'000'000'000, 1'000'000'000);
i32 n = 1'000, k = 500;
std::vector<i32> A(n);
for (size_t i = 0; i < A.size(); ++i)
A[i] = distr(rng);
//for (auto x: A) std::cout << x << " "; std::cout << std::endl;
auto tim = Time();
i32 res = Solve(A, n, k);
tim = Time() - tim;
std::cout << "Result " << res << ", time " << std::fixed
<< std::setprecision(4) << tim << " sec" << std::endl;
}
int main() {
Test();
}
Output:
Result 295187479, time 1.0335 sec