Apply Operations to Make Sum of Array Greater Than or Equal to k
MedPrompt
You are given a positive integer k. Initially, you have an array nums = [1].
You can perform any of the following operations on the array any number of times (possibly zero):
- Choose any element in the array and increase its value by
1. - Duplicate any element in the array and add it to the end of the array.
Return the minimum number of operations required to make the sum of elements of the final array greater than or equal to k.
Example 1:
Input: k = 11
Output: 5
Explanation:
We can do the following operations on the array nums = [1]:
- Increase the element by
1three times. The resulting array isnums = [4]. - Duplicate the element two times. The resulting array is
nums = [4,4,4].
The sum of the final array is 4 + 4 + 4 = 12 which is greater than or equal to k = 11.
The total number of operations performed is 3 + 2 = 5.
Example 2:
Input: k = 1
Output: 0
Explanation:
The sum of the original array is already greater than or equal to 1, so no operations are needed.
Constraints:
1 <= k <= 105
Approaches
3 approaches with complexity analysis and trade-offs.
This approach involves a straightforward linear scan through all potential values for the elements in the final array. For each potential value v, we calculate the required number of elements c and the corresponding total operations. By iterating through all sensible values of v, we can find the minimum number of operations required.
Algorithm
- Handle the edge case: if
kis 1, the initial array[1]is sufficient, so return 0. - The total number of operations is the sum of increment operations and duplicate operations. A known upper bound for the minimum operations is
k - 1(achieved by either incrementing the initial1tok, or by duplicating the1k-1times). - We can iterate through all possible values
vthat the elements in the final array could have. A reasonable range forvis from 1 tok. - For each value
v, we determine the minimum number of elementscneeded to make the sum at leastk. This is given byc = ceil(k / v), which can be calculated using integer arithmetic asc = (k + v - 1) / v. - The number of increment operations to get value
visv - 1. - The number of duplicate operations to get
celements isc - 1. - The total operations for a given
vis(v - 1) + (c - 1). - We keep track of the minimum total operations found across all tested values of
v. - After checking all
vfrom 1 tok, the minimum value found is the answer.
Walkthrough
The fundamental insight is that the most efficient way to construct the final array is to have all its elements be identical. Let's say we decide to create an array of c elements, each with a value of v.
To achieve this from the initial array [1], we need to perform two types of operations:
- Increment Operations: To change the initial element
1tov, we needv - 1increments. - Duplicate Operations: To get an array of
celements from a single element, we needc - 1duplications.
The total number of operations is (v - 1) + (c - 1).
The sum of the array elements will be v * c. To satisfy the problem condition, we must have v * c >= k.
To minimize operations for a fixed v, we should choose the smallest possible integer c that satisfies the condition. This value is c = ceil(k / v). In integer arithmetic, this is calculated as c = (k + v - 1) / v.
This approach iterates through all possible values of v from 1 up to k, calculates the corresponding c and the total operations, and finds the minimum among them.
class Solution { public int minOperations(int k) { if (k == 1) { return 0; } // An initial upper bound is k-1 operations. // This can be achieved by setting v=k, c=1 (k-1 increments, 0 duplicates) // or v=1, c=k (0 increments, k-1 duplicates). int minOps = k - 1; // Iterate through all possible values 'v' from 1 to k. for (int v = 1; v <= k; v++) { // For a given v, the minimum count 'c' needed is ceil(k/v). int c = (k + v - 1) / v; // Total operations = (increment ops) + (duplicate ops) int currentOps = (v - 1) + (c - 1); minOps = Math.min(minOps, currentOps); } return minOps; }}Complexity
Time
O(k) - The main part of the algorithm is a single loop that runs from 1 to `k`. All operations inside the loop are constant time.
Space
O(1) - We only use a few variables to store the minimum operations and loop counters, regardless of the input size `k`.
Trade-offs
Pros
The logic is simple and easy to understand.
It is guaranteed to find the correct answer for the given constraints.
Cons
This approach can be slow if
kis very large, as the time complexity is directly proportional tok.
This approach optimizes the brute-force method by reducing the search space. It's based on the mathematical property that for the optimal pair of value v and count c, at least one of them must be less than or equal to the square root of k. This drastically cuts down the number of iterations needed.
Algorithm
- The problem is to minimize
(v - 1) + (c - 1)subject tov * c >= k. - The optimal integer solution
(v, c)for this problem will have the property thatmin(v, c) <= sqrt(k). - This insight allows us to avoid checking all
vup tok. We can split the search into two parts:- Cases where
v <= sqrt(k). - Cases where
c <= sqrt(k)(which covers the remaining cases wherev > sqrt(k)).
- Cases where
- First, iterate
vfrom 1 up tofloor(sqrt(k)). For eachv, calculatec = ceil(k/v)and the total operations, updating the minimum. - Second, iterate
cfrom 1 up tofloor(sqrt(k)). For eachc, calculate the requiredv = ceil(k/c)and the total operations, updating the minimum. - The overall minimum found after these two loops is the answer.
Walkthrough
We are trying to minimize the function ops(v, c) = v + c - 2 subject to v * c >= k.
The expression v + c is minimized when v and c are close to each other. For a fixed product v*c = P, the sum v+c is minimized when v = c = sqrt(P). This suggests that our optimal integer solution (v, c) will be close to the point (sqrt(k), sqrt(k)) on the v-c plane.
Let's prove that for an optimal solution (v_opt, c_opt), we must have min(v_opt, c_opt) <= sqrt(k). Assume for contradiction that v_opt > sqrt(k) and c_opt > sqrt(k). Then their product v_opt * c_opt > k. This means the solution is not 'tight'. We could potentially decrease v_opt to v_opt - 1 and still satisfy the sum constraint, while reducing the total operations. A more formal proof shows that one of the two must be less than or equal to sqrt(k).
This observation allows us to limit our search. We only need to check pairs (v, c) where at least one component is small.
- We iterate through all small values of
v(i.e.,vfrom 1 tosqrt(k)). For eachv, we find the bestcand calculate the operations. - We iterate through all small values of
c(i.e.,cfrom 1 tosqrt(k)). For eachc, we find the bestvand calculate the operations. This second loop covers the cases wherevis large (since ifv > sqrt(k), thencmust be small).
class Solution { public int minOperations(int k) { if (k == 1) { return 0; } int minOps = k - 1; int limit = (int) Math.sqrt(k); // Case 1: Iterate through v from 1 to sqrt(k). // This covers all pairs where v is small. for (int v = 1; v <= limit; v++) { int c = (k + v - 1) / v; minOps = Math.min(minOps, (v - 1) + (c - 1)); } // Case 2: Iterate through c from 1 to sqrt(k). // This covers all pairs where c is small (and v is large). // Note: There is some overlap with Case 1, but it's harmless. for (int c = 1; c <= limit; c++) { int v = (k + c - 1) / c; minOps = Math.min(minOps, (v - 1) + (c - 1)); } return minOps; }}Complexity
Time
O(sqrt(k)) - The algorithm performs two loops, each running up to `sqrt(k)` times. This is a major improvement over the linear scan.
Space
O(1) - Constant extra space is used.
Trade-offs
Pros
Significantly faster than the O(k) brute-force approach.
Still relatively simple to implement.
Cons
Slightly more complex to reason about than the simple brute-force approach.
This is the most efficient approach, which treats the problem as finding the minimum of a mathematical function. The number of operations, when viewed as a function of the element value v, is unimodal (it first decreases and then increases). This property allows for a ternary search, which can find the minimum in logarithmic time, making it extremely fast.
Algorithm
- The function to minimize is
f(v) = (v - 1) + (ceil(k / v) - 1). - This function is unimodal, meaning it has a single minimum value over the search range
[1, k]. - We can use ternary search to find the value of
vthat minimizes this function. - Initialize a search range
low = 1andhigh = k. - Repeatedly shrink the search range:
- Calculate two midpoints
m1 = low + (high - low) / 3andm2 = high - (high - low) / 3. - Compare
f(m1)andf(m2). - If
f(m1) < f(m2), the minimum must lie in the interval[low, m2], so we sethigh = m2. - Otherwise, the minimum must lie in
[m1, high], so we setlow = m1.
- Calculate two midpoints
- Continue this until the range
[low, high]is very small (e.g.,high - low < 3). - Finally, perform a linear scan over the small remaining interval
[low, high]to find the exact minimum.
Walkthrough
The cost function we want to minimize is f(v) = (v - 1) + (ceil(k / v) - 1). Let's analyze its components:
v - 1: This term increases linearly withv.ceil(k / v) - 1: This term decreases asvincreases.
The sum of a strictly increasing function and a strictly decreasing function results in a convex or unimodal function. The ceil operation introduces some steps, but the overall shape remains unimodal. This means there's a single valley, and we can use an efficient search algorithm to find its bottom.
Ternary search is designed for this exact scenario. It's similar to binary search but divides the search space into three parts instead of two. By evaluating the function at two points (m1 and m2), it can eliminate one-third of the search space in each step, leading to a logarithmic time complexity.
class Solution { public int minOperations(int k) { if (k == 1) { return 0; } long low = 1, high = k; long minOps = k - 1; // Initial upper bound // Ternary search to find the 'v' that minimizes the operations. // The function f(v) = (v-1) + (ceil(k/v)-1) is unimodal. while (high - low >= 3) { long m1 = low + (high - low) / 3; long m2 = high - (high - low) / 3; if (calculateOps(m1, k) < calculateOps(m2, k)) { high = m2; } else { low = m1; } } // After the loop, the minimum is in the small range [low, high]. // We check this range exhaustively. for (long v = low; v <= high; v++) { minOps = Math.min(minOps, calculateOps(v, k)); } return (int) minOps; } // Helper function to calculate operations for a given value 'v'. private long calculateOps(long v, int k) { if (v == 0) return Long.MAX_VALUE; long c = (k + v - 1) / v; // c = ceil(k/v) return (v - 1) + (c - 1); }}Complexity
Time
O(log k) - Ternary search reduces the search space of size `k` by a constant factor at each step.
Space
O(1) - The search is done in-place with a few variables.
Trade-offs
Pros
Extremely efficient, with logarithmic time complexity.
It is the most optimal solution for this problem.
Cons
The implementation is more complex than iterative solutions.
It relies on correctly identifying the unimodal property of the cost function.
Solutions
Solution
class Solution {public int minOperations(int k) { int ans = k; for (int a = 0; a < k; ++a) { int x = a + 1; int b = (k + x - 1) / x - 1; ans = Math.min(ans, a + b); } return ans; }}class Solution {public: int minOperations(int k) { int ans = k; for (int a = 0; a < k; ++a) { int x = a + 1; int b = (k + x - 1) / x - 1; ans = min(ans, a + b); } return ans; }};class Solution: def minOperations(self, k: int) -> int: ans = k for a in range(k): x = a + 1 b = (k + x - 1) // x - 1 ans = min(ans, a + b) return ansVideo walkthrough
Newsletter
One sharp idea, every week
System design and interview prep — short enough to finish.
No spam. Unsubscribe anytime.
Practice
Same difficulty — related problems to reinforce the pattern.