Solving Questions with Brainpower - Leetcode 2140 - Python

NeetCodeIO · Intermediate ·⚡ Algorithms & Data Structures ·1y ago

Key Takeaways

The video solves LeetCode problem 2140 using dynamic programming and recursive backtracking, implementing a 01 knapsack pattern to maximize points earned from solving questions based on their point value and brain power required, utilizing Python for the solution.

Full Transcript

I told you I was going to stop making these leak code videos, but I just couldn't help myself today. I'm not sure why they gave us a dynamic programming problem for the beginning of the month. But I do want to show you that this problem is very, very easy once you understand this pattern. It's literally a textbook problem and I was able to solve it in like 5 minutes with both solutions, the top down and bottom up solution. I'm not even kidding. And I want to kind of tell you how you can get just as good as me by knowing like the patterns and learning based on the patterns. You don't have to use my courses if you don't want to, but I want to show you that this is literally the a 01 knapsack pattern and um logging in so I can see all of this. But yeah, it's literally a textbook problem. The template the exact same that I'm going to be following in today's problem. It's got uh some variations. There's many different variations of this problem. Coin change is considered one of them, but I think coin change is actually the unbounded knapsack. But anyways, let's get into it. Idea here is pretty simple. We're given a bunch of questions. Each question is going to be a pair of values. I'm going to go kind of fast, so please try to keep up. Um, the pair is going to be first the number of points that we can earn from that question, and second is going to be the the amount of brain power it requires for us to solve that question. So you could think of it as this is like our reward function and this is like our cost function. I mean not literally this is not machine learning but you could kind of think of it as the reward and the cost. We want as much of the reward and we want to minimize the cost. So you might think well maybe is this greedy? Whoops my camera was in the way. Sorry about that. You might think okay is this greedy? And it's not. And the reason I know that immediately is again just knowing that this is the 01 knapsack pattern. Now they tell us um the way that this u cost is going to work is we are allowed to start solving questions from left to right. And so if we decide for a question to solve it it's going to cost us and for each question we're going to have two choices. We can either solve it or we can skip it. So again that is a hint that this is dynamic programming or at the very least you should realize that this problem can be solved recursively with backtracking. And when I say that you should be able to recognize this, I mean like if you're familiar with this or if you're preparing for interviews and you want to know if you're good enough to solve these types of problems. If you're just starting out or if you're preparing, yeah, of course, you're not going to know how to do all these little things. But anyway, so that's the general idea. So we're going to kind of have two choices, right? So in this case, we could choose that we get 3 2 or we could skip it in which case we end up at the next uh choice. So 43 and in that case again we could either choose it 43 or we could skip it. So I'll just put a little line for skipping. Now the interesting thing is that when we do choose something so if we chose this one we got a reward of three but we it costed us two. That cost means that we have to skip the next two questions. So at that point the choice that we would have is either to uh solve the question or to skip it. And the question this time would not be either of these. It would be this one. 25. And then the five is going to cost us. So we have to skip the next five questions. But the five questions do not exist. There aren't any more questions left. So this is sort of a base case. So along this path, we total up all of the points that we got and that was five. Among all of these paths, we want to maximize the possible result. So that will be easy to do after each recursive step. We just take the max of both sides and then return the max. So we do that even from the perspective of the subpros. So for example here this would have returned nothing because that at that point we were out of bounds. So that's the general idea behind this problem. I won't even kind of mention memorization or caching because I think that's honestly something you can just kind of figure out by looking at the code that this code is going to be easy to cache and that once we do add caching there's going to be n subp problems starting from the beginning of each of these and so the overall time complexity is going to be big O of N as well as the space complexity is also going to be big O of N. And you're probably wondering well how am I going to do the bottom up solution? I'm actually not going to do a drawing explanation for that because it's actually very very easy to translate this code into a bottomup solution. You don't even need to turn your brain on. I usually just turn mine off when I'm doing that part. Okay, so let's code this up. And by the way, I just realized that I also solved this problem a couple years ago. I think I go a bit more in depth in that explanation. So if you prefer that one, you might be able to just search for it on YouTube. But this one, the solution is going to be mostly the same. So I'm going to have my index, right? And so we know that the base case mainly is when the index goes out of bounds. So if I is let's say equal to the length of the input array. So that means it's exactly out of bounds. At that point we have nothing to solve. So we can just return zero. And then after that we have two main choices. We can call backtrack like we have the skip case and then we have the choose case where we actually choose to solve that problem. Skipping is pretty easy because then we just go to the next index. Uh but choosing is a bit harder. We're actually going to need something for that. We're going to need to unpack the question at index i. So I'm going to say questions index i. I'm going to get the points first and then the brain power. And because when we choose, we not only take the result of the sub problem, which is going to be at index i plus the brain power. And we actually also need a plus one here because we're skipping this many questions. We're not going to advance this many places. is we're going to advance one plus that so that we skip that many questions and we will get this much points. So we take points and add it to the result of this sub problem because this is the question we chose to solve. This is the sub problem. Now if we skipped it we wouldn't have gotten any points but we would have then had the sub problem over here. So among these two that's what we're trying to maximize. So I could do this. I could say well max of these two and then just move them inside. And don't forget the comma over here. And then this is what I could return. So this is the most brute force solution. I'm actually going to run it just to make sure that it works and I didn't miss anything. But we're going to call backtrack from index zero on the outside. Actually remember when we naively said this index is exactly out of bounds. Well remember that we're not only advancing the index by one. We could advance it by more than one. So we might actually end up even further out of bounds than this. It's easy to remedy that. You just do greater than or equal and then return zero. So I think this should be good now. And as expected, we do get a time limit exceeded cuz this is an exponential solution. But you notice that inside of the function, we're just doing constant work. If you don't include like the recursive step, we're not really having any loops or anything like that in here. So we could cache the result of this function call because we know that the number of times it's going to be called is n different times for indexes 0 to n minus one where that's the length of the array. So we could have a little cache over here. We could use a hashmap or we could use an array. I'm going to use an array. So 0 time length of the input because we know that for any of the sub problems the result should never be zero. That's why I'm setting it to zero. We could have also done negative 1, but we know that every question is going to have a positive number of points. So for none of the sub problems, we should ever have a max where it's going to be zero. So then down here we can add another case where if the value at index i is non zero. That means we already solved the sub problem, we can return the result of the sub problem which will be stored in that array. Otherwise, we will compute it and before we return, we're going to set the uh result in that array and then we're going to return it uh just like that. So, this is uh the entire uh caching code. So, let's just give this a run. You can see it didn't really require many changes to optimize it. And there you go. Now, you can see it works. It's pretty efficient. If you want to do the bottom up solution, I'll leave this code here and I'll show you how easy it is for this problem. Notice how when we go top down, we are solving the smallest sub problem first and then we return the result of that and then we can solve bigger problems. We can use that same idea iteratively. We can iterate over the array in reverse order and try to solve each sub problem. So I'm going to do this and then I'm just going to wrap that in a reversed call which should allow us to solve these in reverse order just like this. So if you're confused on what to do if you do it in reverse order the step should be pretty similar to what we did down here. So I'm going to unpack uh those values and then I'm trying to determine what to put here at cache and index i. I want to take the maximum of something. So I'm actually going to store those in variables. I'm going to have my choose and I'm going to have my skip. So for choosing, it's going to be similar to what we had down here. So points plus the sub problem. I'm just going to copy and paste it because I can show you how I'm going to change this around. So we have the points, but instead of making a recursive call, we expect that the sub problem has already been computed. That's why we're going through these in reverse order. So instead of making a recursive call, we're going to do cache at index i + one plus brain power. Now the catch here is that what if this is out of bounds in the recursive solution? We handle that with the base case. It's a little bit harder to handle that here, but it's still possible. I'm going to use a turnary operator. You could do it a bit differently if you want to be more explicit, but I think a turnary is fine. We want to do this if the index is inbounds. Otherwise, we want zero. So, we can say if this is less than the length of questions and actually I'm going to put that in a variable as well. So, I'm going to call that my next index. Maybe I could call it J. Um, but I think this is fine. So, just to kind of have this line be a bit shorter. We'll do that. And then I'll do this over here. And I think for the length of questions, I'm going to change that to n to make this a bit more readable. So, I'm just kind of working fast. I hope you're able to still understand what I'm doing. Um, and lastly over here, while that's inbounds, otherwise we want this to be a zero. So, I'm going to wrap that in parenthesis. So, now we've guarded this to prevent an index out of bounds error down here. Similar, we don't add points. Um, but we will do cache at index i + 1. But again, we want to make sure that that's inbound. So I'm going to actually just copy and paste this and then change next index to be i + one. So it's pretty similar. And then we take the max of choose and skip. And I think we're pretty much done there. And then what we return at the end is going to be cache of zero. Just like uh in the recursive solution we returned backtracking of zero. So I believe this code is good. Let's give it a quick run. And yes, you can see it works. And this one is a bit more efficient in terms of the big O runtime and complexity. It's the same, but usually iterative solutions are faster than recursive solutions because we don't have the recursive function call overhead and all that. So, if you found this helpful, check out neatcode.io for a lot more. Thanks for watching and I'll see you soon.

Original Description

🚀 https://neetcode.io/ - A better way to prepare for Coding Interviews 🧑‍💼 LinkedIn: https://www.linkedin.com/in/navdeep-singh-3aaa14161/ 🐦 Twitter: https://twitter.com/neetcode1 ⭐ BLIND-75 PLAYLIST: https://www.youtube.com/watch?v=KLlXCFG5TnA&list=PLot-Xpze53ldVwtstag2TL4HQhAnC8ATf 0:00 - Read the problem 0:30 - Drawing Explanation 4:40 - Coding Brute Force 7:00 - Coding Memoization 8:35 - Coding DP leetcode 2140 #neetcode #leetcode #python
Watch on YouTube ↗ (saves to browser)
Sign in to unlock AI tutor explanation · ⚡30

Playlist

Uploads from NeetCodeIO · NeetCodeIO · 0 of 60

← Previous Next →
1 Leetcode 149 - Maximum Points on a Line - Python
Leetcode 149 - Maximum Points on a Line - Python
NeetCodeIO
2 Design Linked List - Leetcode 707 - Python
Design Linked List - Leetcode 707 - Python
NeetCodeIO
3 Minimum Time to Collect All Apples in a Tree - Leetcode 1443 - Python
Minimum Time to Collect All Apples in a Tree - Leetcode 1443 - Python
NeetCodeIO
4 Design Browser History - Leetcode 1472 - Python
Design Browser History - Leetcode 1472 - Python
NeetCodeIO
5 Number of Good Paths - Leetcode 2421 - Python
Number of Good Paths - Leetcode 2421 - Python
NeetCodeIO
6 Flip String to Monotone Increasing - Leetcode 926 - Python
Flip String to Monotone Increasing - Leetcode 926 - Python
NeetCodeIO
7 Maximum Sum Circular Subarray - Leetcode 918 - Python
Maximum Sum Circular Subarray - Leetcode 918 - Python
NeetCodeIO
8 Find Closest Node to Given Two Nodes - Leetcode 2359 - Python
Find Closest Node to Given Two Nodes - Leetcode 2359 - Python
NeetCodeIO
9 Concatenated Words - Leetcode 472 - Python
Concatenated Words - Leetcode 472 - Python
NeetCodeIO
10 Data Stream as Disjoint Intervals - Leetcode 352 - Python
Data Stream as Disjoint Intervals - Leetcode 352 - Python
NeetCodeIO
11 LFU Cache - Leetcode 460 - Python
LFU Cache - Leetcode 460 - Python
NeetCodeIO
12 N-th Tribonacci Number - Leetcode 1137
N-th Tribonacci Number - Leetcode 1137
NeetCodeIO
13 Best Team with no Conflicts - Leetcode 1626 - Python
Best Team with no Conflicts - Leetcode 1626 - Python
NeetCodeIO
14 Greatest Common Divisor of Strings - Leetcode 1071 - Python
Greatest Common Divisor of Strings - Leetcode 1071 - Python
NeetCodeIO
15 Shortest Path in a Binary Matrix - Leetcode 1091 - Python
Shortest Path in a Binary Matrix - Leetcode 1091 - Python
NeetCodeIO
16 Insert into a Binary Search Tree - Leetcode 701 - Python
Insert into a Binary Search Tree - Leetcode 701 - Python
NeetCodeIO
17 Delete Node in a BST - Leetcode 450 - Python
Delete Node in a BST - Leetcode 450 - Python
NeetCodeIO
18 Shuffle the Array (Constant Space) - Leetcode 1470 - Python
Shuffle the Array (Constant Space) - Leetcode 1470 - Python
NeetCodeIO
19 Fruits into Basket - Leetcode 904 - Python
Fruits into Basket - Leetcode 904 - Python
NeetCodeIO
20 Number of Subarrays of size K and Average Greater than or Equal to Threshold - Leetcode 1343 Python
Number of Subarrays of size K and Average Greater than or Equal to Threshold - Leetcode 1343 Python
NeetCodeIO
21 Naming a Company - Leetcode 2306 - Python
Naming a Company - Leetcode 2306 - Python
NeetCodeIO
22 As Far from Land as Possible - Leetcode 1162 - Python
As Far from Land as Possible - Leetcode 1162 - Python
NeetCodeIO
23 Shortest Path with Alternating Colors - Leetcode 1129 - Python
Shortest Path with Alternating Colors - Leetcode 1129 - Python
NeetCodeIO
24 Minimum Fuel Cost to Report to the Capital - Leetcode 2477 - Python
Minimum Fuel Cost to Report to the Capital - Leetcode 2477 - Python
NeetCodeIO
25 Count Odd Numbers in an Interval Range - Leetcode 1523 - Python
Count Odd Numbers in an Interval Range - Leetcode 1523 - Python
NeetCodeIO
26 Contains Duplicate II - Leetcode 219 - Python
Contains Duplicate II - Leetcode 219 - Python
NeetCodeIO
27 Path with Maximum Probability - Leetcode 1514 - Python
Path with Maximum Probability - Leetcode 1514 - Python
NeetCodeIO
28 Add to Array-Form of Integer - Leetcode 989 - Python
Add to Array-Form of Integer - Leetcode 989 - Python
NeetCodeIO
29 Unique Paths II - Leetcode 63 - Python
Unique Paths II - Leetcode 63 - Python
NeetCodeIO
30 Minimum Distance between BST Nodes - Leetcode 783 - Python
Minimum Distance between BST Nodes - Leetcode 783 - Python
NeetCodeIO
31 Design Hashmap - Leetcode 706 - Python
Design Hashmap - Leetcode 706 - Python
NeetCodeIO
32 Range Sum Query Immutable - Leetcode 303 - Python
Range Sum Query Immutable - Leetcode 303 - Python
NeetCodeIO
33 Binary Tree Zigzag Level Order Traversal - Leetcode 103 - Python
Binary Tree Zigzag Level Order Traversal - Leetcode 103 - Python
NeetCodeIO
34 Middle of the Linked List - Leetcode 876 - Python
Middle of the Linked List - Leetcode 876 - Python
NeetCodeIO
35 Course Schedule IV - Leetcode 1462 - Python
Course Schedule IV - Leetcode 1462 - Python
NeetCodeIO
36 Single Element in a Sorted Array - Leetcode 540 - Python
Single Element in a Sorted Array - Leetcode 540 - Python
NeetCodeIO
37 Capacity to Ship Packages - Leetcode 1011 - Python
Capacity to Ship Packages - Leetcode 1011 - Python
NeetCodeIO
38 IPO - Leetcode 502 - Python
IPO - Leetcode 502 - Python
NeetCodeIO
39 Minimize Deviation in Array - Leetcode 1675 - Python
Minimize Deviation in Array - Leetcode 1675 - Python
NeetCodeIO
40 Longest Turbulent Array - Leetcode 978 - Python
Longest Turbulent Array - Leetcode 978 - Python
NeetCodeIO
41 Last Stone Weight II - Leetcode 1049 - Python
Last Stone Weight II - Leetcode 1049 - Python
NeetCodeIO
42 Construct Quad Tree - Leetcode 427 - Python
Construct Quad Tree - Leetcode 427 - Python
NeetCodeIO
43 Find Duplicate Subtrees - Leetcode 652 - Python
Find Duplicate Subtrees - Leetcode 652 - Python
NeetCodeIO
44 Sort an Array - Leetcode 912 - Python
Sort an Array - Leetcode 912 - Python
NeetCodeIO
45 Ones and Zeroes - Leetcode 474 - Python
Ones and Zeroes - Leetcode 474 - Python
NeetCodeIO
46 Remove Duplicates from Sorted Array II - Leetcode 80 - Python
Remove Duplicates from Sorted Array II - Leetcode 80 - Python
NeetCodeIO
47 Maximum Twin Sum of a Linked List - Leetcode 2130 - Python
Maximum Twin Sum of a Linked List - Leetcode 2130 - Python
NeetCodeIO
48 Concatenation of Array - Leetcode 1929 - Python
Concatenation of Array - Leetcode 1929 - Python
NeetCodeIO
49 Symmetric Tree - Leetcode 101 - Python
Symmetric Tree - Leetcode 101 - Python
NeetCodeIO
50 Check Completeness of a Binary Tree - Leetcode 958 - Python
Check Completeness of a Binary Tree - Leetcode 958 - Python
NeetCodeIO
51 Construct Binary Tree from Inorder and Postorder Traversal - Leetcode 106 - Python
Construct Binary Tree from Inorder and Postorder Traversal - Leetcode 106 - Python
NeetCodeIO
52 Find Peak Element - Leetcode 162 - Python
Find Peak Element - Leetcode 162 - Python
NeetCodeIO
53 Accounts Merge - Leetcode 721 - Python
Accounts Merge - Leetcode 721 - Python
NeetCodeIO
54 Binary Tree Preorder Traversal (Iterative) - Leetcode 144 - Python
Binary Tree Preorder Traversal (Iterative) - Leetcode 144 - Python
NeetCodeIO
55 Binary Tree Postorder Traversal (Iterative) - Leetcode 145 - Python
Binary Tree Postorder Traversal (Iterative) - Leetcode 145 - Python
NeetCodeIO
56 Number of Zero-Filled Subarrays - Leetcode 2348 - Python
Number of Zero-Filled Subarrays - Leetcode 2348 - Python
NeetCodeIO
57 Minimum Score of a Path Between Two Cities - Leetcode 2492 - Python
Minimum Score of a Path Between Two Cities - Leetcode 2492 - Python
NeetCodeIO
58 Sqrt(x) - Leetcode 69 - Python
Sqrt(x) - Leetcode 69 - Python
NeetCodeIO
59 Successful Pairs of Spells and Potions - Leetcode 2300 - Python
Successful Pairs of Spells and Potions - Leetcode 2300 - Python
NeetCodeIO
60 Optimal Partition of String - Leetcode 2405 - Python
Optimal Partition of String - Leetcode 2405 - Python
NeetCodeIO

This video teaches how to solve LeetCode problem 2140 using dynamic programming and recursive backtracking, and how to optimize the solution using caching and bottom-up approaches. The problem involves selecting questions to solve based on their point value and brain power required, and the solution utilizes Python. By watching this video, learners can improve their skills in dynamic programming, recursive backtracking, and systems design.

Key Takeaways
  1. Use recursive backtracking to find the maximum possible result
  2. Calculate the total points earned along each path
  3. Maximize the possible result by taking the max of both sides
  4. Use a hashmap or array to cache the result of the function call
  5. Cache the result of the function call to avoid exponential solution
  6. Use a bottom-up solution to solve the problem iteratively
  7. Use a turnary operator to handle out of bounds index in the recursive solution
  8. Change the line to make it shorter
  9. Replace question length with n for readability
  10. Guard against index out of bounds error
💡 Using caching and bottom-up approaches can significantly optimize the solution to dynamic programming problems, reducing exponential time complexity to linear or polynomial time complexity.

Related AI Lessons

Bloom Filters, Explained Properly
Learn how Bloom filters work and their benefits, including tiny memory and blazing speed, in exchange for potential false positives.
Dev.to · Daksh Gargas
Prefix Sums: The Preprocessing Trick That Makes Range Queries Instant
Learn how prefix sums enable instant range queries in arrays, boosting performance in various applications
Medium · Programming
I Thought I Was Ready for the Interview — Then One Simple Math Question Destroyed Me
A simple math question can destroy a developer's interview, highlighting the importance of being prepared for unexpected questions
Medium · Programming
Week 2(Day 10): LeetCode Two Pointers(slow & fast): Remove Duplicates from Sorted Array (Brute…
Learn to remove duplicates from a sorted array using the two pointers technique, improving from brute force to optimized solutions
Medium · Python

Chapters (5)

Read the problem
0:30 Drawing Explanation
4:40 Coding Brute Force
7:00 Coding Memoization
8:35 Coding DP
Up next
Stump Grinder Carbide Wheel Grinds Hardwood To Chips
Innoforge Studio
Watch →