2233. Maximum Product After K Increments
Problem Description
The problem provides us with a list of non-negative integers named nums
and an integer k
. The task is to increase any element of nums
by 1
during each operation, and you can perform this operation at most k
times. The goal is to maximize the product of all the elements in the array nums
. After finding the maximum product, we need to return the result modulo 10^9 + 7
because the maximum product could be a very large number. The core of the problem lies in strategically choosing which numbers to increment to maximize the final product.
Intuition
In order to maximize the product of the numbers, we want to even out the values as much as possible. This is because the product of numbers that are closer together is generally higher than the product of the same numbers that are not evenly spread. For example, the product of [4, 4, 4] is 64, while the product of [2, 4, 6] is only 48, even though both sets have the same sum of 12.
With this in mind, we arrive at the solution approach:
-
Use a min-heap (a data structure that allows us to always extract the smallest number efficiently) to manage the numbers. This ensures that we can always increment the smallest number available, which helps in balancing them as evenly as possible.
-
Perform
k
operations of incrementing the smallest value in the min-heap. After each increment, the updated number is pushed back into the heap, maintaining the heap order. -
Once all the operations are done, calculate the product of all the elements in the heap. We have to keep in mind the modulo
10^9 + 7
during this step by taking the modulo after each multiplication to prevent integer overflow.
By following this approach, we effectively distribute the increments in a way that pushes the product to its maximum possible value before taking the modulo.
Learn more about Greedy and Heap (Priority Queue) patterns.
Solution Approach
The solution to this problem is implemented in Python and makes use of the heap data structure, which allows us to easily and efficiently access and increment the smallest element in the nums
list. Here is the step-by-step explanation of the implementation:
-
Heapify the List: The first step is to convert the list
nums
into a min-heap using theheapify
function from Python'sheapq
module. In a min-heap, the smallest element is always at the root, which allows us to apply our increments as effectively as possible.heapify(nums)
-
Perform Increment Operations: Next, we perform
k
increments. For each operation, we extract the smallest element from the heap usingheappop(nums)
, increment it by1
, and then push it back into the heap usingheappush(nums, heappop(nums) + 1)
. This ensures that our heap remains in a consistent state, with the smallest element always on top, ready to be incremented in the next operation.for _ in range(k): heappush(nums, heappop(nums) + 1)
-
Calculate the Product: Once all increments are done, we iterate through all elements in the heap to calculate the product. Because we're looking for the product modulo
10^9 + 7
, we take the modulo after each multiplication to prevent integer overflow. We initialize theans
variable to1
and iterate through each valuev
innums
, applying the modulo operation as we multiply:ans = 1 mod = 10**9 + 7 for v in nums: ans = (ans * v) % mod
-
Return the Result: Finally, we return the calculated
ans
as the result.
The key algorithm used here is the heap (priority queue), which allows us to prioritize incrementing the smallest numbers. The pattern is simple yet effective: by giving priority to the smallest elements for incrementation, we use the operations to balance the numbers, aiming for a more uniform distribution which leads to a maximized product. The implementation is careful to take the product modulo 10^9 + 7
at each step, ensuring that the final result is within the correct range and doesn't overflow.
Ready to land your dream job?
Unlock your dream job with a 2-minute evaluator for a personalized learning plan!
Start EvaluatorExample Walkthrough
Let's walk through a small example to illustrate the solution approach.
Imagine we have nums = [1, 2, 3]
and k = 3
.
-
Heapify the List: We convert
nums
into a min-heap.Starting array: [1, 2, 3]
After heapify: [1, 2, 3] (Note: Since the array is already a valid min-heap, there's no visible change)
-
Perform Increment Operations: We are allowed 3 increments.
1st increment: Smallest is 1. We pop 1 out, increment to 2, and push back to heap.
Heap after 1st increment: [2, 2, 3]
2nd increment: Now we have two 2s at the top. We pop one out, increment to 3, and push back.
Heap after 2nd increment: [2, 3, 3]
3rd increment: One more increment to the remaining 2.
Heap after 3rd increment: [3, 3, 3]
We've used our 3 increments to even out the numbers, as predicted by our intuition.
-
Calculate the Product: Now we calculate the product of the heap's elements modulo
10^9 + 7
.Starting with
ans = 1
,For 3:
ans = (1 * 3) % 1000000007 = 3
For next 3:
ans = (3 * 3) % 1000000007 = 9
For last 3:
ans = (9 * 3) % 1000000007 = 27
So the final product modulo
10^9 + 7
is 27. -
Return the Result: The result, 27, is returned as the final output.
Solution Implementation
1from heapq import heapify, heappop, heappush
2
3class Solution:
4 def maximumProduct(self, nums, k):
5 # Convert the list nums into a min-heap in-place
6 heapify(nums)
7
8 # Increment the smallest element in the heap k times
9 for _ in range(k):
10 smallest = heappop(nums) # Pop the smallest element
11 heappush(nums, smallest + 1) # Increment the smallest element and push back onto heap
12
13 # Calculate the product of all elements mod 10^9 + 7
14 product = 1
15 modulo = 10**9 + 7
16 for num in nums:
17 product = (product * num) % modulo
18
19 return product
20
1class Solution {
2
3 // Define the MOD constant to use for avoiding integer overflow issues.
4 private static final int MOD = (int) 1e9 + 7;
5
6 // Function to calculate the maximum product of array elements after
7 // incrementing any element 'k' times.
8 public int maximumProduct(int[] nums, int k) {
9 // Initialize a min-heap (PriorityQueue) to store the elements.
10 PriorityQueue<Integer> minHeap = new PriorityQueue<>();
11
12 // Add all the elements to the min-heap.
13 for (int num : nums) {
14 minHeap.offer(num);
15 }
16
17 // Increment the smallest element in the heap 'k' times.
18 while (k-- > 0) {
19 // Retrieve and remove the smallest element from the heap,
20 // increment it and add it back to the heap.
21 int incrementedValue = minHeap.poll() + 1;
22 minHeap.offer(incrementedValue);
23 }
24
25 // Initialize the answer as a long to prevent overflow during the computation.
26 long answer = 1;
27
28 // Calculate the product of all elements now in the heap.
29 while (!minHeap.isEmpty()) {
30 // Take each element from the heap, multiply it with the current answer
31 // and compute the modulus.
32 answer = (answer * minHeap.poll()) % MOD;
33 }
34
35 // Return the final product modulo MOD as an integer.
36 return (int) answer;
37 }
38}
39
1#include <vector>
2#include <algorithm>
3#include <functional> // for std::greater
4
5class Solution {
6public:
7 int maximumProduct(std::vector<int>& nums, int k) {
8 const int mod = 1e9 + 7; // Modulo value for the final result
9
10 // Create a min-heap from the given vector nums
11 std::make_heap(nums.begin(), nums.end(), std::greater<int>());
12
13 // Iteratively increment the smallest element and then reheapify
14 while (k-- > 0) {
15 std::pop_heap(nums.begin(), nums.end(), std::greater<int>());
16 ++nums.back(); // Increment the smallest element
17 std::push_heap(nums.begin(), nums.end(), std::greater<int>()); // Reheapify
18 }
19
20 // Compute the product of all elements modulo mod
21 long long product = 1; // Use long long to avoid integer overflow
22 for (int v : nums) {
23 product = (product * v) % mod; // Update the product with each element
24 }
25
26 return static_cast<int>(product); // Cast to int to match return type
27 }
28};
29
1import { MinPriorityQueue } from '@datastructures-js/priority-queue';
2
3/**
4 * Calculates the maximum product of an array after incrementing any element "k" times.
5 * @param {number[]} nums An array of numbers.
6 * @param {number} k The number of increments to perform.
7 * @returns {number} The maximum product modulo 10^9 + 7.
8 */
9function maximumProduct(nums: number[], k: number): number {
10 const n: number = nums.length;
11 let priorityQueue: MinPriorityQueue<number> = new MinPriorityQueue<number>();
12
13 // Initialize the priority queue with all elements from the nums array
14 for (let i = 0; i < n; i++) {
15 priorityQueue.enqueue(nums[i]);
16 }
17
18 // Increment the smallest element in the queue k times
19 for (let i = 0; i < k; i++) {
20 let minElement: number = priorityQueue.dequeue().element;
21 priorityQueue.enqueue(minElement + 1);
22 }
23
24 let product: number = 1;
25 const MODULO: number = 10 ** 9 + 7;
26
27 // Calculate the product of all elements in the queue
28 for (let i = 0; i < n; i++) {
29 product = (product * priorityQueue.dequeue().element) % MODULO;
30 }
31
32 return product;
33}
34
35// Note: This code assumes that MinPriorityQueue<number> is imported correctly and available.
36
Time and Space Complexity
Time Complexity
The time complexity of this code is determined by the following parts:
-
Heapify Operation: Converting the
nums
array into a min-heap has a time complexity ofO(n)
wheren
is the number of elements innums
. -
K Pop and Push Operations: We pop the smallest element and then push an incremented value back onto the heap,
k
times. Each such operation might takeO(log n)
time since in the worst case, the element might need to sift down/up the heap which is a tree of heightlog n
. Therefore, the time complexity for this part isO(k log n)
. -
Final Iteration to Calculate Product: We iterate over the heap of size
n
once and do a multiplication each time. Since the heap is already a valid heap structure, and we are simply iterating over it, the iteration takesO(n)
time.
Thus, the overall time complexity is O(n + k log n + n)
= O(n + k log n)
, assuming k
pop and push operations dominate for larger values of k
.
Space Complexity
The space complexity is determined by:
-
Heap In-Place: Since the heap is constructed in-place, it does not require additional space proportional to the input size beyond the initial list. Therefore, we consider this
O(1)
additional space. -
Intermediate Variables: Only a constant amount of extra space is used for variables like
ans
andmod
, which is alsoO(1)
.
Therefore, the space complexity of the algorithm is O(1)
.
Learn more about how to find time and space complexity quickly using problem constraints.
Which data structure is used in a depth first search?
Recommended Readings
Greedy Introduction div class responsive iframe iframe src https www youtube com embed WTslqPbj7I title YouTube video player frameborder 0 allow accelerometer autoplay clipboard write encrypted media gyroscope picture in picture web share allowfullscreen iframe div When do we use greedy Greedy algorithms tend to solve optimization problems Typically they will ask you to calculate the max min of some value Commonly you may see this phrased in the problem as max min longest shortest largest smallest etc These keywords can be identified by just scanning
https algomonster s3 us east 2 amazonaws com cover_photos heap svg Priority Queue and Heap What is the relationship between priority queue and heap Priority Queue is an Abstract Data Type and Heap is the concrete data structure we use to implement a priority queue Priority Queue A priority queue
LeetCode Patterns Your Personal Dijkstra's Algorithm to Landing Your Dream Job The goal of AlgoMonster is to help you get a job in the shortest amount of time possible in a data driven way We compiled datasets of tech interview problems and broke them down by patterns This way we
Want a Structured Path to Master System Design Too? Don’t Miss This!