Find the Minimum Amount of Time to Brew Potions
MedPrompt
You are given two integer arrays, skill and mana, of length n and m, respectively.
In a laboratory, n wizards must brew m potions in order. Each potion has a mana capacity mana[j] and must pass through all the wizards sequentially to be brewed properly. The time taken by the ith wizard on the jth potion is timeij = skill[i] * mana[j].
Since the brewing process is delicate, a potion must be passed to the next wizard immediately after the current wizard completes their work. This means the timing must be synchronized so that each wizard begins working on a potion exactly when it arrives.
Return the minimum amount of time required for the potions to be brewed properly.
Example 1:
Input: skill = [1,5,2,4], mana = [5,1,4,2]
Output: 110
Explanation:
| Potion Number | Start time | Wizard 0 done by | Wizard 1 done by | Wizard 2 done by | Wizard 3 done by |
|---|---|---|---|---|---|
| 0 | 0 | 5 | 30 | 40 | 60 |
| 1 | 52 | 53 | 58 | 60 | 64 |
| 2 | 54 | 58 | 78 | 86 | 102 |
| 3 | 86 | 88 | 98 | 102 | 110 |
As an example for why wizard 0 cannot start working on the 1st potion before time t = 52, consider the case where the wizards started preparing the 1st potion at time t = 50. At time t = 58, wizard 2 is done with the 1st potion, but wizard 3 will still be working on the 0th potion till time t = 60.
Example 2:
Input: skill = [1,1,1], mana = [1,1,1]
Output: 5
Explanation:
- Preparation of the 0th potion begins at time
t = 0, and is completed by timet = 3. - Preparation of the 1st potion begins at time
t = 1, and is completed by timet = 4. - Preparation of the 2nd potion begins at time
t = 2, and is completed by timet = 5.
Example 3:
Input: skill = [1,2,3,4], mana = [1,2]
Output: 21
Constraints:
n == skill.lengthm == mana.length1 <= n, m <= 50001 <= mana[i], skill[i] <= 5000
Approaches
2 approaches with complexity analysis and trade-offs.
This approach is based on dynamic programming principles. We derive a recurrence relation for the optimal start time S_j for each potion j. The key insight is that the entire processing pipeline for a potion might need to be delayed to ensure no wizard is required to work on two potions simultaneously. The optimal delay between potion j-1 and j is determined by finding the most constrained wizard, which requires checking all n wizards. This leads to an O(n) calculation for each of the m potions.
Algorithm
- Let
S_jbe the start time for potionj(when wizard 0 begins). We can assumeS_0 = 0. - The time wizard
ifinishes potionj,finish_time(i, j), can be expressed in terms ofS_jand prefix sums ofskill. LetP_i = skill[0] + ... + skill[i]. Thenfinish_time(i, j) = S_j + mana[j] * P_i. - A wizard
imust be free to start potionj. This implies that the pipeline for potionjmust not overtake the pipeline for potionj-1. This gives the constraintfinish_time(i-1, j) >= finish_time(i, j-1)fori > 0, andstart_time(0, j) >= finish_time(0, j-1)fori=0. - Substituting the formula for
finish_timegives a lower bound forS_jbased onS_{j-1}:S_j >= S_{j-1} + max_{i=0..n-1} (mana[j-1] * P_i - mana[j] * P_{i-1}), whereP_{-1}is taken as 0. - To minimize total time, we choose the earliest possible start time for each potion. We can compute
S_jiteratively:S_j = S_{j-1} + max(0, C_j), whereC_jis themaxterm from the previous step. - First, precompute the prefix sums
Pof theskillarray inO(n)time. - Initialize
current_S = 0(representingS_{j-1}). - Loop
jfrom 1 tom-1. In each iteration: a. CalculateC_jby iteratingifrom 0 ton-1and finding the maximum ofmana[j-1] * P_i - mana[j] * P_{i-1}. This takesO(n)time. b. Updatecurrent_Sby addingmax(0, C_j). - The final result is the finish time of the last wizard on the last potion, which is
S_{m-1} + mana[m-1] * P_{n-1}.
Walkthrough
The core of this method is to find the minimum required start time S_j for each potion j sequentially. We start with S_0 = 0. For each subsequent potion j (from 1 to m-1), we calculate the minimum necessary delay after potion j-1 finishes. This delay is determined by the 'bottleneck' wizard, i.e., the wizard i for whom the constraint finish_time(i-1, j) >= finish_time(i, j-1) imposes the largest delay on S_j relative to S_{j-1}. We iterate through all wizards to find this maximum required delay, update the start time for potion j, and repeat until we have the start time for the last potion. The total time is then easily calculated.
All intermediate products and sums can exceed the capacity of a 32-bit integer, so it's crucial to use 64-bit integers (long in Java) for these calculations to avoid overflow.
class Solution { public long minimumTime(int[] skill, int[] mana) { int n = skill.length; int m = mana.length; // Step 1: Compute prefix sums of skill long[] p = new long[n]; p[0] = skill[0]; for (int i = 1; i < n; i++) { p[i] = p[i-1] + skill[i]; } long currentS = 0; // This will hold S_{j-1} at the start of the loop // Step 2: Iterate through potions to find their start times for (int j = 1; j < m; j++) { long maxTerm = 0; // Find the maximum delay term C_j for (int i = 0; i < n; i++) { long p_i = p[i]; long p_i_minus_1 = (i == 0) ? 0 : p[i-1]; long term = (long)mana[j-1] * p_i - (long)mana[j] * p_i_minus_1; if (term > maxTerm) { maxTerm = term; } } // Update the start time for the current potion j currentS += maxTerm; } // Step 3: Calculate final finish time return currentS + (long)mana[m-1] * p[n-1]; }}Complexity
Time
O(n * m) The algorithm involves pre-calculating prefix sums in `O(n)`. Then, it iterates `m-1` times. Inside this loop, there is another loop that runs `n` times to find the maximum delay term. This results in a total time complexity of `O(n + m*n)`, which simplifies to `O(n*m)`.
Space
O(n) We use an array of size `n` to store the prefix sums of the `skill` array.
Trade-offs
Pros
It's a correct and relatively intuitive approach once the underlying dynamic relationship between potion start times is understood.
Easier to implement compared to more optimized geometric approaches.
Cons
The time complexity of
O(n*m)might be too slow and result in a 'Time Limit Exceeded' error if bothnandmare large (e.g., close to 5000).
Video 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.