Topics
Given an array a
of n
integers and a budget k
. You can perform an operation at most k
times: choose an element and increase or decrease it by 1. Find the minimum possible difference between the maximum and minimum elements in the final array.
Idea
The core idea is to use binary search on the answer, the minimum possible difference d
. If it’s possible to achieve a difference d
within k
operations, it’s also possible to achieve any difference d' > d
(potentially with fewer operations). This monotonic property allows us to binary search for the smallest feasible d
.
Approach 1 (Prefix sums + Binary Search)
For a given difference d
, we need to determine if it’s possible to make the final difference between the maximum and minimum elements at most d
using <= k
operations. Sorting the array a
makes it easier to calculate costs. Also, precomputing the prefix sums pref
of the sorted array allows us to calculate the sum of elements in a range [i, j]
in time as pref[j+1] - pref[i]
.
To achieve a difference d
, we need to find a target range [min_val, max_val]
such that max_val - min_val <= d
. The total cost involves increasing elements smaller than min_val
to min_val
and decreasing elements larger than max_val
to max_val
, i.e.,
Key Insight
In an optimal final configuration, either the minimum value (
min_val
) or the maximum value (max_val
) corresponds to one of the original values in the sorted arraya[i]
.
Thus, we can iterate through each a[i]
in the sorted array and:
- Case 1: Assume
min_val = a[i]
. The target range is[a[i], a[i] + d]
.- Calculate cost to increase elements
a[0...i-1]
toa[i]
:cost_inc = i * a[i] - pref[i]
. - Calculate cost to decrease elements
a[q...n-1]
toa[i] + d
, wherea[q]
is the first element> a[i] + d
. Findq
usingupper_bound
.cost_dec = (pref[n] - pref[q]) - (n - q) * (a[i] + d)
. - If
cost_inc + cost_dec <= k
, then differenced
is possible. Returntrue
.
- Calculate cost to increase elements
- Case 2: Assume
max_val = a[i]
. The target range is[a[i] - d, a[i]]
.- Calculate cost to increase elements
a[0...p-1]
toa[i] - d
, wherea[p]
is the first element>= a[i] - d
. Findp
usinglower_bound
.cost_inc = p * (a[i] - d) - pref[p]
. - Calculate cost to decrease elements
a[q...n-1]
toa[i]
, wherea[q]
is the first element> a[i]
. Findq
usingupper_bound
.cost_dec = (pref[n] - pref[q]) - (n - q) * a[i]
. - If
cost_inc + cost_dec <= k
, then differenced
is possible. Returntrue
.
- Calculate cost to increase elements
- If the loop completes without finding a suitable range, return
false
.
Time Complexity: where is range of binary search which’s for given problem.
Space Complexity: due to prefix sum array
Approach 2: Two Pointers
This approach leverages two pointers at opposite ends technique. The key steps:
- Sort the array
- Use two pointers (
i=0
,j=n-1
) converging toward the median. This is a greedy approach - For each pair
(v[i], v[j])
, the optimal cost to make their difference ⇐d
ismax(0, (v[j] - v[i]) - d)
- Sum these costs for all pairs: if total ⇐
k
,d
is feasible
Example:
Original array: [1, 3, 5, 7, 9]
Pairs: (1,9), (3,7), (5)
Target d=3:
- (1,9) needs 9-1-3 = 5 ops
- (3,7) needs 7-3-3 = 1 op
- Total: 6 ops
Why it works?
Fixing pairs in desc order of their differences, gives maximal reduction in total operations
Time Complexity: (sorting) + (binary search with linear check)
Space Complexity: (no extra space beyond input)
Code
// Approach 1 (Binary Search)
ll calculate_cost_inc(ll target_min, const vector<int> &arr,
const vector<ll> &pref) {
// Find index p such that arr[p] >= target_min (first element not less than
// target_min)
auto it = lower_bound(all(arr), target_min);
int p = distance(arr.begin(), it);
// Sum for arr[0]...arr[p-1]
return p * target_min - pref[p];
}
ll calculate_cost_dec(ll target_max, const vector<int> &arr,
const vector<ll> &pref) {
// Find index q such that arr[q] > target_max (first element greater than
// target_max)
int n = arr.size();
auto it = upper_bound(all(arr), target_max);
int q = distance(arr.begin(), it);
// Sum for arr[q]...arr[n-1]
return (pref[n] - pref[q]) - (n - q) * target_max;
}
bool check(ll mid, vector<int> &arr, vector<ll> &pref, ll k) {
int n = arr.size();
if (n == 0)
return true; // Edge case: empty array
for (int i = 0; i < n; ++i) {
// Case 1: Test range [arr[i], arr[i] + mid]
ll cost1 = calculate_cost_inc(arr[i], arr, pref) +
calculate_cost_dec(arr[i] + mid, arr, pref);
if (cost1 <= k) {
return true;
}
// Case 2: Test range [arr[i] - mid, arr[i]]
ll cost2 = calculate_cost_inc(arr[i] - mid, arr, pref) +
calculate_cost_dec(arr[i], arr, pref);
if (cost2 <= k) {
return true;
}
}
return false;
}
void solve() {
int n;
ll k;
cin >> n >> k;
vector<int> arr(n);
vector<ll> pref(n + 1, 0);
for (int i = 0; i < n; ++i) {
cin >> arr[i];
}
sort(all(arr));
for (int i = 0; i < n; ++i) {
pref[i + 1] = arr[i] + pref[i];
}
ll lo = 0;
ll hi = 1e9 + 5;
ll ans = hi;
while (lo <= hi) {
ll mid = lo + (hi - lo) / 2;
if (check(mid, arr, pref, k)) {
ans = mid;
hi = mid - 1;
} else {
lo = mid + 1;
}
}
cout << ans << endl;
}
// Approach 2 (Two pointers)
void solve() {
ll n, k;
cin >> n >> k;
vector<ll> v(n);
ll ans = 0, lo = 0, hi = -1;
for (auto &e : v) {
cin >> e;
hi = max(hi, e);
}
sort(all(v));
auto check = [&](ll mid) -> bool {
ll diff_to_make = 0;
for (ll i = 0; i < n / 2; i++) {
ll curr_diff = v[n - i - 1] - v[i];
diff_to_make += max(0ll, curr_diff - mid);
}
return diff_to_make <= k;
};
while (lo <= hi) {
ll mid = lo + (hi - lo) / 2;
if (check(mid)) {
ans = mid;
hi = mid - 1;
} else
lo = mid + 1;
}
cout << ans << endl;
}
Related