🚀 neetcode.io/ - A better way to prepare for Coding Interviews
@aaditkamat49952 жыл бұрын
We can also initialize the array with amount instead of amount + 1 right, because the maximum number of coins used will be in the case where each coin you use is a 1 coin?
@jagdish-main2 жыл бұрын
@@aaditkamat4995 what if you have to form amount 10 and you have only 1 unit coin , than total number of coins will be 10 which is equal to the amount
@MP-ny3ep Жыл бұрын
Love how you went from the greedy approach to the brute force dfs approach to the optimal dp approach. It helped me understand this problem better. Thank you !
@perelium-x3 ай бұрын
For those having a hard time with dp just some advice 1. Decision Trees 2. Recursion 3. Optimal Substructure and Overlapping Subproblem then just Memoize you have urself dp tc
@adithyagowda46423 жыл бұрын
The way you explained DFS approach and converted it into a DP solution was just freaking amazing!!! It's exactly what I was looking for. I now feel confident in converting my DFS solutions into DP solution. Thank you very much!
@richard123457893 жыл бұрын
actually neet's explanation to compare dfs,greedy and then using dp is soo rad and lowkey!! man u are awesome. give me a few more days i am gonna start donating for ur coffee's each week
@NeetCode3 жыл бұрын
Thanks appreciate it a lot 😊
@Nahwka4 ай бұрын
@richard12345789 Have you started donating. 😂?
@wilsonwang86412 жыл бұрын
The part at 12:20 really helps me to clarify the problem. Thanks bro Neetcode, you produce really meaningful content.
@MIDNightPT43 жыл бұрын
Dynamic Programming is so confusing!!!!!!
@ehm-wg8pd10 ай бұрын
yes its hard for me at begining, you should try with cases that has shaloe level of recursive and learn from there
@ivandrofly9 ай бұрын
YET beautiful :D
@muammarajohnson5336Ай бұрын
@@ehm-wg8pd create an alg that does everything. every version. the uglliest brute force, no shortcuts, no cleverness, version you would scream at someone for implementing. then add memoization.
@cody_code2 жыл бұрын
Wow, I was so stumped on this concept until I found this video. Thanks for going the extra mile with the explanation!
@DoJoStan4 жыл бұрын
Excellent explanation, I was trying to wrap my head around the bottom up approach solution described on leetcode and you illustrated it perfectly.
@lcgrind6 ай бұрын
same! I was lost at what dp[i - coin[j]] meant, and it finally clicked when I watched this video at the @13:30s mark. Ty!
@satyajeetkumarjha14822 жыл бұрын
Your dp code is similar to what most of us will code but your code is more readable. Highly appreciable .
@pomegranate85932 жыл бұрын
excellent, I love how patient and carefully u explain instead of rushing stuff and skipping steps
@inquisitorshampoo10434 жыл бұрын
Have my amazon interview in 10 minutes. Your videos have been incredibly helpful while studying!
@chuongtruong70904 жыл бұрын
How was your interview ?
@inquisitorshampoo10434 жыл бұрын
@@chuongtruong7090 good! ended up getting an offer
@chuongtruong70904 жыл бұрын
@@inquisitorshampoo1043 Wow, congrats !!!!
@EDROCKSWOO4 жыл бұрын
@@inquisitorshampoo1043 goddamn so lucky. I got rejected by google, a week ago. I cry.
@mastermind54213 жыл бұрын
@@EDROCKSWOO what questions did they ask you?
@rjtwo54342 жыл бұрын
I was so close here but this helped to sort it out for me. Hands down the best explanations on this stuff. Thank you so much for this!
@ahmadbasyouni91737 ай бұрын
your channel saves me so much time than trying to read the weird wording of the editorial on LC appreciate you man!
@wlcheng2 жыл бұрын
Brilliant explanation! I was able to come up with the code by myself after understanding your solution. And the implementation is quite similar to yours. Guess I followed your coding style pretty well. 😆
@yipeng62392 жыл бұрын
You are more qualified than most university instructors on introducing DSA topics, no joke
@souravchakraborty7002 жыл бұрын
Anyone curious about top to bottom recursion(backtracking with memoization) : def coinChange(self, coins: List[int], amount: int): memo = {} def dfs(amount): if amount == 0: return 0 if amount
@vyshakr30582 жыл бұрын
thanks i was actually searching for this
@skyfly67Ай бұрын
I wonder, isn't this solution more memory efficient? When I got the video right, the bottom-up solution calculates a solution for every value from 1-amount, even those that are not reachable given the provided coins. So for coins = [100] and amount = 99, the top-down solution would immediately return, while the bottom-up calculates "not possible" for every amount 1-99. Am I wrong? It feels like I should be :D
@KevinN443 жыл бұрын
Awesome explanation! I was stuck with the top down approach and how to convert it to the DP solution, and this made it all click. Thanks!
@jagdish-main2 жыл бұрын
I watched this problem in so many videos but all of them was forcing me to remember things and making this problem complex but you made it so intuitive like it's damm easy, thanks for helping :)
@linli70493 жыл бұрын
When I search for a solution for a problem on leetcode, I will watch your video first. Excellent explanation!
@rachelwilliams85695 ай бұрын
For the range you are looping over in line 6, you don't need to go from 1..amount + 1. You only need to loop from 1 to the amount since you already covered 0 before the loop.
@madixit752 жыл бұрын
Impressed, I was going nuts with leet code premium explanation. I owe you👌
@woostanley62908 ай бұрын
This is such a hard question and I am really thankful that you are here to help to explain how to proceed with dp problems...
@himeshsylesh2 жыл бұрын
You had made the best explanation found on youtube for this problem, with 3 different approaches. Thanks, buddy.
@emachine0037 ай бұрын
Thanks for the help... I figured out top-down memoization on my own, so fortunately most of my work carried over to the bottom-up approach.
@airman1224697 ай бұрын
There’s also a simple recursive way of doing this problem. Take the amount of change you’re looking for, find the largest denomination that does not go over the amount of change being requested, subtract that denomination from the amount requested, and call the same function with the new value. To account for values that are higher than the largest denomination, simply take the change amount value and subtract the integer division value that results from the change requested divided by the largest denomination, and again, call the function recursively.
@keystarr6 ай бұрын
thank you, man, couldn't solve on my own and didn't understand both the Editorial and one of the top submissions. Only your explanation did it for me!
@TheSRONIX2 жыл бұрын
Thank you so much for the thought process of how to get to the dynamic programming solution!
@halahmilksheikh2 жыл бұрын
For anyone curious, here's what the top down DP with memoization looks like. It's very slow. If you remove the memoization, you get a time limit exceeded but it still "works" var coinChange = function(coins, amount) { let memo = [] // with memoization let ret = dfs(amount) if (ret == Infinity) ret = -1 return ret function dfs(target) { // returns min from all paths if (target < 0) { return Infinity } if (target == 0) { return 0 } if (memo[target] != null) { return memo[target] } memo[target] = Infinity for (let coin of coins) { memo[target] = Math.min(memo[target], 1 + dfs(target - coin)) } return memo[target] } };
@mastermax7777 Жыл бұрын
why is it slow? I wrote it in python and it was just as fast as the dp solution he gave us...
@PippyPappyPatterson Жыл бұрын
@@mastermax7777 Agreed, shouldn't it be the same complexity? It only checks the number of paths to `0` once for each `range(amount)`.
@qualityhumour15102 жыл бұрын
Your explanations skills are really amazing! Definitely very helpful! 🤓
@andrewwong8483 Жыл бұрын
Really good explanation of the 1+dp[a] and what the 1 means in this case- because we’re using one coin as we’re iterating through the coins
@kwaminaessuahmensah89203 жыл бұрын
Bro, your explanations are so good. Keep up the amazing work!
@SaurabhGupta-ei2hl3 жыл бұрын
Best Explanation on Dynamic Programming in a Scientific Approach!! 🙌🏻
@ramonjales99412 жыл бұрын
i'm from Brazil. And you gained more one subscriber.Because your explanation is very good and you talk so cleary that i can undestand you. And this is so good for me that i'm learning english.
@alessandrocamillo49393 жыл бұрын
Great video, great explanation. It starts from the brute force approach to build up he required intuition to eventually realize we can use a DP based solution. Thank you very much. One thing I wish to comment is that I am now curious to see a DP top-down recursive solution based on memoization. ;)
@namelesslamp123 жыл бұрын
Me too since i am new to dp memoization sometimes it's not clear to me :)
@GauravDhiman2 жыл бұрын
Here is the python DP top-down recursive solution with memoization. It took me some time to come up with it. class Solution: def __init__(self): self.memo = {} self.coins = None def coinChange(self, coins: List[int], amount: int) -> int: if self.memo.get(amount) != None: return self.memo[amount] if amount == 0: return 0 if self.coins == None: self.coins = sorted(coins) minc = float("inf") for c in self.coins: if amount < c: break res = self.coinChange(coins=None, amount=amount-c) if res == -1: continue minc = min(res + 1, minc) res = -1 if minc == float('inf') else minc self.memo[amount] = res return res
@siddharthsharma36793 жыл бұрын
Dude !!! I don't usually drop comments but this was amazing ! Crisp explanation, thank you soo much.
@spoorthi52303 жыл бұрын
Thank you! Definitely better explanation than the leetcode's explanation!
@NeetCode3 жыл бұрын
Glad it was helpful!
@juliahuanlingtong67573 жыл бұрын
Good explantion starting from recursion tree. However, during the transformation from recursion to bottom up dp, I really tried to search the answer to my 2 confusion, yet failed to find it. 1. How did dp resolve the problem of dupliactes, eg, 1,5,1 and 1,1,5 are essentially the same and if we do through recursion, we have to add logic ro avoid that, but why dp arrays don't have to? 2. How does it evolevs into a knapsack (take or not take ) problem? In another word, why dp[a]=min(dp[a], dp[a-c])? Looking forward to your reply to rsolve my huge confusions. Thanks!
@sumitevs3 жыл бұрын
i have the same doubt. why dp[a]=min(dp[a], dp[a-c])?
@JasonKim13 жыл бұрын
Duplicates don't matter here because what is cached is the minimum coins used. With your example, DP[7] is 3 whichever coin options are used. If the question has asked, return all the possible combinations of coins that make up the minimum number of coins uses, and the question explicitly asks you to dedupe cases like 1,1,5 and 1,5,1, then you have to care about dupes.
@RohithMusic Жыл бұрын
@@sumitevs I think we can think of it in a different way. dp[a] = 1+min(dp[a-c1],dp[a-c2], ...dp[a-ci]) where 1 to i is the different denominations of coins given. It looks confusing in code because he is doing a loop over ci and storing the min in dp[a] itself after every iteration.
@utberoxsobad3 жыл бұрын
I got this question in an Amazon technical interview. I wish I saw this video beforehand
@germanou9 ай бұрын
Dude, your explanation ability is over the top. Amazing!
@George-nx8zu2 жыл бұрын
Dynamic Programming is always so fuzzy to me. Thank you for explaining!
@n-julkushwaha2827 Жыл бұрын
one of the best solutions and explanation. Even though i knew the solution but still got stunned by your approach........
@georgpohl26652 жыл бұрын
Very well explained. Thank you i finally understood this problem. Keep at it!
@srinadhp3 жыл бұрын
Thanks again! Your explanation helped a great deal in understanding these complex problems.
@jackchan62662 жыл бұрын
i commented on the code for my learning hopefully this is helpful class Solution(object): def coinChange(self, coins, amount): """ :type coins: List[int] :type amount: int :rtype: int """ dp = [amount + 1] * (amount + 1) # 0 to amount so amount + 1 in total dp[0] = 0 # base case, amount 0 takes 0 coins print(dp) #bottom up order for a in range(1, amount + 1): # to get to each number in 1 to the amount + 1 for c in coins: # go through every coin if a - c >= 0: # remainder dp[a] = min(dp[a], 1 + dp[a - c]) # go through every possible solution # coin = 4, a = 7, dp[7] = 1 + dp[7-4] = 1 + dp[3] # dp stores the number of coins needed to get to that number, therefore we get 1 for in 1 + dp[a - c] for an additional coin needed print(dp) return dp[amount] if dp[amount] != amount + 1 else -1 # dp[amount] != amount + 1 is the default value
@aadityakiran_s Жыл бұрын
15:53 This is false! If you set each value to infinity, it will lead to an overflow when you do the Math.Min(dp[i], dp[i - coins[j]] + 1); That + 1 will cause it to overflow to a large -ve number and that will throw your answer off by a large margin. Otherwise, you're right it could be any large value BUT with the constraints of the question, the only safe value we can put it amount + 1 since the min denomination of a coin is 1 and that would cause our total number of coins to be at max amount only.
@sayanghosh6996 Жыл бұрын
inf+1 is still inf. He is using python not c++
@aadityakiran_s Жыл бұрын
@@sayanghosh6996 Oh, in python it's like that is it? I didn't know. Thanks for the info man.
@nikhilaradhya40886 ай бұрын
Good. You can start at min coin value in the outer for loo. It takes O(len(coins)) extra time to find the min. Helps in cases where amount is too large and number of coins of small.
@RobertPodosek Жыл бұрын
This is brilliant. Really helped me understand dynamic programming. Thanks!
@akhilraj6891 Жыл бұрын
We can make the code work in constant space complexity by just storing the answer for last five amounts when calculate the current amount.
@yunusemreozvarlik29064 ай бұрын
Clean and concise explanation. Thanks!
@The6thProgrammer Жыл бұрын
If you are struggling to understand why we updated dp[i] when (i - coins[j] >= 0) the key insight is that dp[i] only gets updated if there is a solution that leads to zero (even when this condition evaluates to true). That is, there are cases where (i - coins[j] >= 0) but dp[i] remains "amount + 1". For instance if we have amount = 11 and coins = [2, 4, 6], when we get to i = 3, we find that 3 - 2 >= 0. However, min(dp[3], 1 + dp[3 - 2]) causes dp[3] to remain "amount + 1" as there is no valid solution leading to zero that we captured at dp[1] (that is, dp[1] itself still equals "amount + 1").
@CarlosNexans4 ай бұрын
BFS with a Bitmap is also accepted, with the same complexity. Almost the same memory footprint but DP is a lot faster. Sequential memory access is very efficient.
@hamzabendi97513 жыл бұрын
I don't think this solution would work in all cases. Let's say you can only use the coins (2, 5, 10) to return a value of 2, at the first iteration and when trying to calculate dp[1] you're gonna find out that no coin is smaller that 1 so the condition "a - c >= 0" will not be met, and so dp[1] will stay equal to amount+1 (and this is gonna screw what comes next). So if you try to know the number of coins needed to return a value of 2 (it should be 1 cause we have 2 in our list of coins), this algorithm will return -1 because you will have dp[2] = amount+1. I'm not sure if i explained my process of thoughts right, if you think i'm wrong i'd be happy to learn.
@mk-19memelauncher653 жыл бұрын
Dp2 will be min of maxInt and 1+ dp[2-2] which hits the basecase of 0 and returns 1 correctly
@Techgether6 ай бұрын
Love the clear and concise explanation!
@koubbe2 жыл бұрын
I know this problem is best solved by using Dynamic Programming, but I want to focus on the Brute Force solution: I can say it is Divide and Conquer: We are dividing the problem into smaller sub-problems, solving individually and using those individual results to construct the result for the original problem. I can also say it is Backtracking: we are enumerating all combinations of coin frequencies which satisfy the constraints. I know both are implemented via Recursion, but I would like to know which paradigm the Brute Force solution belongs to: Divide and Conquer or Backtracking.
@symbol7673 жыл бұрын
Sick, I tried this for the first time on my own and used BFS to find the "shortest path". Sadly I knew I needed to optimize it with caching but couldn't figure out the right way to implement it. When I saw I only needed 3 extra lines to finish my solution I felt hella dumb. But this is a really good problem.
@psychogalvanometer89452 жыл бұрын
Finally found a solution that I can understand. Thank you Sir!
@LumartCh8 ай бұрын
Great explanation, regardless I'm still trying to understand the whole concept 😅
@ahmeeeeeeeeeeeed4 жыл бұрын
Great video, deserves at least one million views in my opinion.
@NeetCode4 жыл бұрын
Thanks, i appreciate it
@hudsonriverrunner3 жыл бұрын
for those who wanna know the optimal, check bidirectional BFS with DP, it beats 99.6%
@observer6983 жыл бұрын
? how?
@Babe_Chinwendum2 жыл бұрын
Mann, my first approach was greedy. Thanks for explaining why it did not work out.
@mrpotato20272 жыл бұрын
Extremely well explained. Thank you so much!
@bikcrum2 жыл бұрын
I was able to code myself after looking at your video. Great explanation.
@CasualyinAir3 жыл бұрын
Thank you. Your explanation is really clear.
@gn033986042 жыл бұрын
It's a pretty clear solution. really helps me a lot! appreciate!!!
@enisarik60022 жыл бұрын
First understand DFS very well, and then try to understand bottom-up DP concept. Coding is trivial, but understanding the whole solution is really hard.
@georgechen11249 ай бұрын
Well explained, bro! Impressive DP solution!
@johnhammer86682 жыл бұрын
Thankyou very much. The DFS part is just amazing. It was visually intuitive.
@AfterThisShutUp7 ай бұрын
This can also be solved using breadth-first search since we are trying to find the shortest path from amount to 0.
@惠雨-g8q Жыл бұрын
Really clear pronunciation! Also the explanations!
@superheroherohero2 жыл бұрын
Thank you, very very clear, your videos have very high quality.
@Why_I_am_a_theist2 жыл бұрын
One more thing, if you initialize dp[0] = 1 then you don't need to do 1+ dp[a-c] and this is okay because what is the no of ways to make sum 0 out of n no of coins , the answer is 1 i.e don't consider any coin
@danielsun7162 жыл бұрын
couple questions: 1. dp[a] = min(dp[a], 1 + dp[a - c]), why should compare itself with 1 + dp[a-c]? is it possible dp[a] it self less than 1 + dp[a- c]? 2. why the condition is if dp[amount] != amount + 1:? if coins = [2], amount = 3, how should it explain under this situation? I am so confused.
@Asmrnerd10 Жыл бұрын
The correct solution on leetcode has ur OUTER loop as its INNER LOOP and your INNER loop as its OUTER loop. Why is it switched around? For example the solution on leetcode says this: Bottom up DP class Solution: def coinChange(self, coins: List[int], amount: int) -> int: dp=[math.inf] * (amount+1) dp[0]=0 for coin in coins: for i in range(coin, amount+1): if i-coin>=0: dp[i]=min(dp[i], dp[i-coin]+1) return -1 if dp[-1]==math.inf else dp[-1]
@kennyelkhart Жыл бұрын
It’s just enumerating the coins first. The time complexity is the same and so is the result, as the correct minimum is still discovered.
@Nerdimo2 жыл бұрын
Definitely a confusing one, I hope sometime in the future I understand it better. Thanks for the tip
@kobebyrant94833 жыл бұрын
Great explanation. Crystal clear!
@stockholmsyndromeself-trea75172 жыл бұрын
Yo... Kobe...
@muammarajohnson5336Ай бұрын
as a team lead, this would be much more readable if you named your arguments for coin in coins instead of c, the added mental energy just to remember that c is coins.... which i have to go check now cause i forgot if that were true. outside of theat great video. DP is just brute force recursion and memoization.
@muammarajohnson5336Ай бұрын
the added mental energy takes away from whats being taught.
@rajmalla5310Ай бұрын
thankyou brother, helped a lot
@ashleywilkonson3864 жыл бұрын
Could you do the DP solution for Partition to K Equal Sum Subsets? There are literally no English explanations for it, only the backtracking approach.
@RanjuRao3 жыл бұрын
crystal clear illustration :) awesome ! Thanks for the video
@nikhilgoyal007 Жыл бұрын
self notes: Not greedy - since 5 + 1 + 1 will not work; backtracking - yea but overlapping subproblems so can use DP; Memoization works ; now think in reverse and do bottoms up .
@edreesomer9851 Жыл бұрын
What is the difference between back tracking and dfs? I know dft in graph but how is it used here?
@billvivino2 жыл бұрын
This video rocks! 🙌
@Call-me-Avi2 жыл бұрын
Pretty neat explanation man. Thank you
@edenrosales82142 жыл бұрын
Are the dynamic programming and dfs solutions different in terms of space or time complexity?
@halahmilksheikh2 жыл бұрын
You literally explained this problem better than my algorithms professor at a UC
@premrr83303 жыл бұрын
Next level explanation! Instantly subscribed! Thank you
@amol_Ай бұрын
IMO, Dp core version first should be discuss, I mean 2D , dp[coins.length + 1][amount + 1] so every row will represent if i consider only first ith coins then what amount i can reach with how many coins ? here repetation is allow so when we loop for required amount instead of previous row, we need to look into same row (it is tricky) incase of no repetation then look previous row for required amount.
@DanielVazquez3 жыл бұрын
Greedy does work if we remove the biggest coin at every full iteration and keep checking for the minimum amount of change. This, however, is pretty inefficient.
@oliver99999-e16 күн бұрын
isn't the DFS solution also DP but top down? since you are using memoization and you have optimal substructure
@evyats9127 Жыл бұрын
Nice and short functional programming style solution: dp = [0] for i in range(1, amount+1): options = [dp[i-x] for x in coins if i >= x and dp[i-x] != -1] dp.append(-1 if not options else 1+min(options)) return dp[amount]
@kennyelkhart Жыл бұрын
What is ‘functional’ about this? Also, why constantly resize an array if you know its size (amount+1) ahead to time?
@adityaverma32474 ай бұрын
What software you use to explain using figures, diagram ? Please elaborate, because its needed for everyone to explain way you do in interviews.
@habibhasanshakil90863 жыл бұрын
Just awesome. subscribed your channel. huge love and respect for you bro.
@jiaruizhu45784 жыл бұрын
your explanation is amazing!
@NeetCode4 жыл бұрын
Thanks!
@kasiruyamagata77162 жыл бұрын
U r just awesome U make complex solutions easy
@stevetom56074 ай бұрын
Best vedio for this issue!
@andreslikesramen3 жыл бұрын
Great Explanation and Visuals
@muhammetguduk20203 жыл бұрын
Wish I saw this question earlier. I'd have nailed the Intel's coding interview ... Only one that I couldn't solve was this one..
@warnercooler44883 жыл бұрын
Awesome explanation! Thank you so much!
@bonle3771 Жыл бұрын
arent both DP? first one seems to be easier to think of. One of the other way I was doin. is to traverse the sorted coins from the