3259. Maximum Energy Boost From Two Drinks
Problem Description
You are given two integer arrays energyDrinkA
and energyDrinkB
, each of length n
, representing the energy boosts per hour provided by two different energy drinks, A and B, respectively. To maximize your total energy boost, you can drink one energy drink per hour. However, switching from one energy drink to the other requires waiting for one hour to cleanse your system, during which you won't receive any energy boost. You can start with either of the two energy drinks. Return the maximum total energy boost achievable in the next n
hours.
Intuition
To solve this problem, we use a dynamic programming approach. We define f[i][0]
as the maximum energy boost obtained up to hour i
when drinking energy drink A at that hour, and f[i][1]
as the maximum energy boost obtained up to hour i
when drinking energy drink B at that hour.
The key challenge is to balance between the energy boost obtained by consistently drinking the same energy drink and the potential benefit of switching drinks, despite the one-hour penalty.
We initialize:
f[0][0]
with the energy boost fromenergyDrinkA[0]
.f[0][1]
with the energy boost fromenergyDrinkB[0]
.
The state transitions are defined as follows for each hour i
from 1 to n-1
:
f[i][0]
is the maximum of continuing with drink A (f[i-1][0] + energyDrinkA[i]
) or switching from drink B (f[i-1][1]
because of the penalty hour).f[i][1]
is the maximum of continuing with drink B (f[i-1][1] + energyDrinkB[i]
) or switching from drink A (f[i-1][0]
).
Finally, the result is the maximum value between f[n-1][0]
and f[n-1][1]
, representing the best possible total energy boost achievable by the end of the n
hours.
Learn more about Dynamic Programming patterns.
Solution Approach
The solution is implemented using a dynamic programming approach. We utilize a 2D list f
to keep track of the maximum energy boost achievable up to each hour i
, with separate entries for choosing energy drink A or B at that hour.
-
Initialization:
- Create a 2D list
f
withn
rows and 2 columns, each initialized to 0. This will store the maximum energy boost for each houri
using either drink A or drink B. - Set
f[0][0]
toenergyDrinkA[0]
, representing the initial choice of drink A. - Set
f[0][1]
toenergyDrinkB[0]
, representing the initial choice of drink B.
- Create a 2D list
-
State Transitions:
- For each hour
i
from 1 ton-1
, compute:f[i][0]
asmax(f[i - 1][0] + energyDrinkA[i], f[i - 1][1])
: the maximum energy boost by either continuing with drink A from the previous hour or switching from drink B after a one-hour wait.f[i][1]
asmax(f[i - 1][1] + energyDrinkB[i], f[i - 1][0])
: similarly, the maximum energy boost by either continuing with drink B from the previous hour or switching from drink A after a one-hour wait.
- For each hour
-
Result:
- Return
max(f[n - 1][0], f[n - 1][1])
to retrieve the highest energy boost possible by the end ofn
hours.
- Return
Here's the code implementing the approach:
class Solution:
def maxEnergyBoost(self, energyDrinkA: List[int], energyDrinkB: List[int]) -> int:
n = len(energyDrinkA)
f = [[0] * 2 for _ in range(n)]
f[0][0] = energyDrinkA[0]
f[0][1] = energyDrinkB[0]
for i in range(1, n):
f[i][0] = max(f[i - 1][0] + energyDrinkA[i], f[i - 1][1])
f[i][1] = max(f[i - 1][1] + energyDrinkB[i], f[i - 1][0])
return max(f[n - 1])
This algorithm efficiently computes the maximum possible energy boost by dynamically deciding whether to switch drinks or continue with the current choice, considering the penalty for switching.
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 the solution approach using a small example. Assume energyDrinkA = [3, 2, 7]
and energyDrinkB = [4, 5, 1]
.
-
Initialization:
- Length of the arrays,
n = 3
. - Create a 2D list
f
with dimensions3 x 2
. Initially,f
is[[0, 0], [0, 0], [0, 0]]
. - Set the base cases:
f[0][0]
=energyDrinkA[0]
= 3f[0][1]
=energyDrinkB[0]
= 4- So,
f
is now[[3, 4], [0, 0], [0, 0]]
.
- Length of the arrays,
-
State Transitions:
- For hour
1
(i = 1
):f[1][0]
=max(f[0][0] + energyDrinkA[1], f[0][1])
=max(3 + 2, 4)
=max(5, 4)
= 5f[1][1]
=max(f[0][1] + energyDrinkB[1], f[0][0])
=max(4 + 5, 3)
=max(9, 3)
= 9f
becomes[[3, 4], [5, 9], [0, 0]]
.
- For hour
2
(i = 2
):f[2][0]
=max(f[1][0] + energyDrinkA[2], f[1][1])
=max(5 + 7, 9)
=max(12, 9)
= 12f[2][1]
=max(f[1][1] + energyDrinkB[2], f[1][0])
=max(9 + 1, 5)
=max(10, 5)
= 10f
updates to[[3, 4], [5, 9], [12, 10]]
.
- For hour
-
Result:
- The maximum total energy boost achievable in the next
3
hours ismax(f[2][0], f[2][1])
=max(12, 10)
= 12.
- The maximum total energy boost achievable in the next
Therefore, the maximum energy boost achievable is 12 units.
Solution Implementation
1from typing import List
2
3class Solution:
4 def maxEnergyBoost(self, energyDrinkA: List[int], energyDrinkB: List[int]) -> int:
5 n = len(energyDrinkA) # Get the number of days or drinks
6 # Initialize a 2D list to store maximum energy potential for each day and each option (A or B)
7 f = [[0] * 2 for _ in range(n)]
8
9 # Base cases for the first day
10 f[0][0] = energyDrinkA[0] # Energy boost if drink A is chosen on the first day
11 f[0][1] = energyDrinkB[0] # Energy boost if drink B is chosen on the first day
12
13 # Iterate through each day starting from the second day
14 for i in range(1, n):
15 # Maximum energy if drink A is chosen on the ith day
16 f[i][0] = max(f[i - 1][0] + energyDrinkA[i], f[i - 1][1])
17 # Maximum energy if drink B is chosen on the ith day
18 f[i][1] = max(f[i - 1][1] + energyDrinkB[i], f[i - 1][0])
19
20 # Return the maximum energy boost possible on the last day from either drink A or B
21 return max(f[n - 1])
22
1class Solution {
2 public long maxEnergyBoost(int[] energyDrinkA, int[] energyDrinkB) {
3 // Get the number of drinks
4 int n = energyDrinkA.length;
5
6 // Initialize a 2D array to store maximum energy collected up to each drink
7 long[][] maxEnergy = new long[n][2];
8
9 // Base case: the first drink's energy is directly taken for both types
10 maxEnergy[0][0] = energyDrinkA[0];
11 maxEnergy[0][1] = energyDrinkB[0];
12
13 // Dynamic programming to fill up the maxEnergy array
14 for (int i = 1; i < n; ++i) {
15 // For drink type A:
16 // 1. Add current energy drink A to the previous max for type A
17 // 2. Choose the previous best result from type B
18 maxEnergy[i][0] = Math.max(maxEnergy[i - 1][0] + energyDrinkA[i], maxEnergy[i - 1][1]);
19
20 // For drink type B:
21 // 1. Add current energy drink B to the previous max for type B
22 // 2. Choose the previous best result from type A
23 maxEnergy[i][1] = Math.max(maxEnergy[i - 1][1] + energyDrinkB[i], maxEnergy[i - 1][0]);
24 }
25
26 // Return the maximum energy collected considering both sequences
27 return Math.max(maxEnergy[n - 1][0], maxEnergy[n - 1][1]);
28 }
29}
30
1class Solution {
2public:
3 long long maxEnergyBoost(vector<int>& energyDrinkA, vector<int>& energyDrinkB) {
4 int n = energyDrinkA.size(); // Determine the number of energy drinks
5 vector<vector<long long>> dp(n, vector<long long>(2));
6
7 // Initialize the first day's potential boost from each drink
8 dp[0][0] = energyDrinkA[0];
9 dp[0][1] = energyDrinkB[0];
10
11 // Iterate through each day, calculating the maximum energy boost
12 for (int i = 1; i < n; ++i) {
13 // Calculate the maximum boost if drinking from energyDrinkA on day i
14 dp[i][0] = max(dp[i - 1][0] + energyDrinkA[i], dp[i - 1][1]);
15
16 // Calculate the maximum boost if drinking from energyDrinkB on day i
17 dp[i][1] = max(dp[i - 1][1] + energyDrinkB[i], dp[i - 1][0]);
18 }
19
20 // Return the maximum boost possible on the last day from either drink
21 return max(dp[n - 1][0], dp[n - 1][1]);
22 }
23};
24
1function maxEnergyBoost(energyDrinkA: number[], energyDrinkB: number[]): number {
2 // Number of energy drinks
3 const n = energyDrinkA.length;
4
5 // Create a 2D array to store the maximum energy boost achievable at each step
6 // f[i][0] represents the max energy boost if using drink A at step i
7 // f[i][1] represents the max energy boost if using drink B at step i
8 const f: number[][] = Array.from({ length: n }, () => [0, 0]);
9
10 // Initialize the first step with the initial energy from drink A and drink B
11 f[0][0] = energyDrinkA[0];
12 f[0][1] = energyDrinkB[0];
13
14 // Iterate through each energy drink starting from the second one
15 for (let i = 1; i < n; i++) {
16 // Calculate the max energy boost when choosing drink A at step i
17 // Option 1: Continue using drink A from the previous step
18 // Option 2: Switch to drink A after using drink B in the previous step
19 f[i][0] = Math.max(f[i - 1][0] + energyDrinkA[i], f[i - 1][1]);
20
21 // Calculate the max energy boost when choosing drink B at step i
22 // Option 1: Continue using drink B from the previous step
23 // Option 2: Switch to drink B after using drink A in the previous step
24 f[i][1] = Math.max(f[i - 1][1] + energyDrinkB[i], f[i - 1][0]);
25 }
26
27 // Return the maximum energy boost obtainable after using all drinks
28 return Math.max(f[n - 1][0], f[n - 1][1]);
29}
30
Time and Space Complexity
The time complexity of the code is O(n)
because it involves a single loop that iterates through the list from start to finish, where n
is the length of the input arrays energyDrinkA
and energyDrinkB
. Each iteration involves constant-time operations.
The space complexity of the code is O(n)
due to the 2D list f
created to store intermediate results. This list requires space proportional to n
, with two entries per element to track the maximum energy boost for selecting either energyDrinkA[i]
or energyDrinkB[i]
at each step.
Learn more about how to find time and space complexity quickly.
Which of the following uses divide and conquer strategy?
Recommended Readings
What is Dynamic Programming Prerequisite DFS problems dfs_intro Backtracking problems backtracking Memoization problems memoization_intro Pruning problems backtracking_pruning Dynamic programming is an algorithmic optimization technique that breaks down a complicated problem into smaller overlapping sub problems in a recursive manner and uses solutions to the sub problems to construct a solution
Coding Interview 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
Recursion Recursion is one of the most important concepts in computer science Simply speaking recursion is the process of a function calling itself Using a real life analogy imagine a scenario where you invite your friends to lunch https algomonster s3 us east 2 amazonaws com recursion jpg You first
Want a Structured Path to Master System Design Too? Don’t Miss This!