The 0/1 Knapsack Problem (Demystifying Dynamic Programming)
HTML-код
- Опубликовано: 1 окт 2024
- 👉 NEW VIDEO & CODE: backtobackswe.... (free)
Free 5-Day Mini-Course: backtobackswe.com
Try Our Full Platform: backtobackswe....
📹 Intuitive Video Explanations
🏃 Run Code As You Learn
💾 Save Progress
❓New Unseen Questions
🔎 Get All Solutions
I was inspired to do this video after seeing that Tuschar Roy had covered this problem. He did a good job, but I feel it very necessary to stress what is really happening and what each cell REALLY means.
Dynamic programming is about subproblems, not remembering patterns to fill cells in with. I watched EVERY ONE of Tuschar Roy's videos and found myself MEMORIZING how to fill out the cells INSTEAD of really knowing what was going on.
I hope this video sheds light on what this problem is really trying to express.
I talked about the bottom up way to do things. Here is the code for that way of doing it: www.sanfoundry...
You can also do it TOP DOWN with recursion where we investigate all expressions of the subproblems to find the optimal solution. The book Elements of Programming Interviews by Aziz Adnan has a very good version of this. The problem is 17.6 in that book.
++++++++++++++++++++++++++++++++++++++++++++++++++
Question: Write a program for the knapsack problem that selects a subset of items that has maximum value and satisfies the weight constraint. All items have integer weights and values. Return the value of the subset.
Can we do it greedily?
0/1 means you cannot split an item. If you could split an item, you could solve this greedily by sorting the item entries by value and then picking from greatest value to least. When you run out of space in your "sack", you'd split the last item and then you would have maximized weight vs value.
Brute Force: We could consider all subsets of items in a complete search and take on the cost of exponential time of 2^n (we will explain this in another video).
Greedy doesn't work, brute forcing won't make the cut, now what? What can we do now?
Dynamic Programming.
Notice that we can subproblem this.
Dynamic programming is not about stupid magic tables that you see people fill out, it is not about guessing. DP is about remembering the solutions to subproblems so that we can find the globally optimal solution. We just subproblemed this recursively.
This is where the table comes from. Each cell MEANS SOMETHING.
IT IS THE ANSWER TO THE QUESTION.
If we solve all the subproblems and remember all answers then we will find the globally optimal answer.
The subproblems are represented by what is called a recurrence equation.
Complexities
n = total items
m = max weight (max weight constraint)
Time: O(nm) (we will be solving this many subproblems)
Space: O(nm) (we will store the results of n*m subproblems)
++++++++++++++++++++++++++++++++++++++++++++++++++
HackerRank: / @hackerrankofficial
Tuschar Roy: / tusharroy2525
GeeksForGeeks: / @geeksforgeeksvideos
Jarvis Johnson: / vsympathyv
Success In Tech: / @successintech
++++++++++++++++++++++++++++++++++++++++++++
The 0/1 Knapsack problem is question 17.6 in the fantastic book Elements of Programming Interviews.
Table of Contents: (my bad if I'm loud, this video is old and the RUclips algo keeps feeding it)
Problem Introduction 0:00 - 2:38
Walkthrough One Subproblem 2:38 - 4:38
The DP Table Introduction 4:38 - 6:12
The Recurrence Relation 6:12 - 8:30
What Each Cell Really Means 8:30 - 9:08
Solving The Dynamic Programming Table 9:08 - 16:46
Finding The Items That We Chose 16:46 - 18:32
Gearing Your Mind For Other DP Problems 18:32 - 19:07
Time & Space Complexities 19:07 - 19:45
Wrap Up 19:45 - 20:10
This is the 0/1 Knapsack Problem. The key is to see the subproblems. DP is just something that takes seeing a lot of problems to get a solid grasp of. I still do not have a full grasp of the subject myself.
Hey man, what's V sub i? from your max() function?
Woooooooooooooooooooow, doode
Really love the passion u show for solving these problems....this is so hard to see these days...feels like u r in a zone or something when u r explaining....kudos & keep it up !!
May u remain as excited !!
@@anshulabhinav13 yo
@@leozhang1340 The value of the i'th item
Someone give that man a medal
hahahaha, the comments I get keep getting funnier
Just came here to thank you again. It's because of your tutorials I got a job at Amazon 😎
@@nitinpathak8763 nice
@@nitinpathak8763 Hey, I have an interview scheduled. I'm done with round 0 (coding test) up for round 1-4. I could really use some tips. Thanks in advance...:)
@@omi04 Hey Omkar!
How u applied for Amazon? Is it via referral or ...? Plz reply
give this man a beer!
yes
Wow you're so brilliant. Thank you for slowing down this problem for me. I was so confused in class 😂
Tis' what we do
I think the best channel related to coding. He explains everything in a really good way.
Thanks a lot Benyam Ephrem.
thanks and thx
This is what my algorithms class is missing
yes
The idea behind the algorithm design should be the core of an algorithm course. The video managed to do that. Bravo!
*It will take a month to grasp this problem*
Me: *Watching it one night before my exam*
Though, I got the idea man! Thank you :)
I love your passion, man. Not only great teaching, it's also fun to watch you!
yeah - I care lol
your explanations are much better than others teaching dp on youtube, not going to name names (I'm sure you know) and you deserve a medal
hahahaha, this is one of the top comments on the channel
You're the Sal Khan of programming interview concepts, my friend. Bless!
Sorta...he is way more pure. I just want to build a large, honest, and effective business that makes people's lives better. He is on another level of purpose and vision. I'd hope to be there someday.
I have seen very few people with this level of passion for teaching....Thank you sir for teaching so passionately!!!
sure lol
Every other RUclipsr: "In this DP problem, just do this math, and the problem is solved."
Back to Back SWE: Explains why we do every step.
I've watched a lot of dynamic programming (DP) videos, but only your videos have given me a clear understanding of each step. Using the thought process from your videos, I have been able to successfully solve all DP problems with complete intuition.
Not having the items in order by weight is a great touch 👌
Thanks for doing that. Intuitively, people would assume they have to which really doesn't change the result.
yes
Initially I assumed dynamic programming as a mind blender remembering all tabular approaches practicing a bit
But......
This one video changed the perspection of dynamic programming, how can we apply this approach to all other problems.
Thanks a lot from bottom of my heart
Please I request you to make more videos on dynamic programming.
One who is perfect in dynamic programming is a master of Data structures and algorithms
You are the one
nice
I think, to approach this problem from DP table perspective is a little difficult intuitively.
My approach is, just recursively solving sub problems like Climbing stairs or Egg Dropping.
First define *base cases*
*Case1* : if the weight capacity is 0, then we can’t choose any item. Hence the optimum value is 0;
*Case2* : if the number of items is 0, then we can’t choose any item. Hence the optimum value is 0;
*Other cases*
*Case3* : if the weight of the Nth Item is greater than the max weight capacity, then, we have only one option, i.e. NOT choose that item, but to choose from remaining(N-1) items for the same weight capacity.
*Case4* : if the weight of Nth item is equal to or lesser than max weight, then we have the 2 options.
Either to choose the item or Not to choose.
But the decision to be made is depending on whether we get max value by choosing Nth Item or not choosing the item but choosing from the remaining (N-1) items.
i.e
If we choose Nth item, then value1 = (value Of Nth Item) + (optimum Value from remaining N-1 items for the remaining weight).
If we don’t choose Nth item, then value2 = (optimum Value from remaining N-1 items for the max weight)
Now our optimum value is max(value1,value2);
private static int optimumValueRecursively(int n, int maxWeight ){
//Base cases 1 and 2
if(maxWeight==0 || i==0){
return 0;
}
//case 3
if(weights[n]>maxWeight){
return optimumValueRecursively2(n-1, maxWeight);
}
//case 4
int optimumValue =
Math.max((values[n]+optimumValueRecursively(n-1, maxWeight-weights[n])),
optimumValueRecursively(n-1, maxWeight));
return optimumValue;
}
There is no DP table involved in the above solution, DP table is useful when we consider memoization.
yes
I just came across your channel and I would like to show my appreciation for what you do. Your energy and enthusiasm when explaining these problems is contagious! Super helpful for someone who is learning programming from scratch. Thank you for your hard work!
im sooooo glad I've found your channel, this problem was giving me a HEADACHE yesterday, and you're the only one who explained it in such a brilliant way so far!!! i hope I also understand the asymptotic notations from you, they've given me a heart ache for the ENTIRE semester ughhh!!
nice
My only issue is how do you come up with this] formula. Just seems like if you seen the problem before you can generate the subproblems from there.
yes that is hard
every time he says it takes me a long time to grasp it. I think he just wants us to feel comfortable...
I mean yeah - things take me a while to grasp too
Excellent ! Greetings from France
I just wanted to make a point clear that here it is a bounded knapsack...so we cannot use the weights in the same row again and again along the row for different weights of the bag..that the reason we go 1 row up when we go "w" weight back in a row...pls correct me if I am wrong..
yes, bounded
you can't imagine how much you helped me with your comment
Thanks a lot.
One knowledge to solve many problems.
Similar approach to solving problems like "Coin change", "Shopping", etc.
I prefer using the recursive DP. I could come up with solution in few minutes.
Thanks for helping me see a different way to think about the problems.
This is the best DP problem explanation video I've ever watched! I can say 90% of instructors in the universities couldn't do better than you bro. Salute! Hope you can continue to make awesome videos!
I wish I could give this more than just one thumbs up, thank you for explaining this so clearly and concisely.
sure
You are the best one I learn from him because you focus on the idea of the solution, not the solution itself and memorizing it. Thank you for your efforts.
thanks
your energy is off the charts and this video was amazing. tysm
lmao my b old video - angry time in this beings life
The im feeling it edit is too real. I know exactly that feeling when you're in the flow of an explanation
hahaha
THAAAAAAK YOU!!!
I WAS TRYING 5 DAYS TO UNDERSTAND THAT WITH NO RESULT!!
great
I have never been this stressed watching a programming problem explained lmao
I love you energy and the enthusiasm you put in to make a person understand. It just really makes me wanna sit and listen. Thanks for videos like these :).
ye - good lol
at the last moment when he says that "it tooks him almost 1 month to undestand" it gives me more satisfaction than anything because still i am thinking that i missed something about this problem i saw almost 15 video still didnt get it properly
yeah i aint smart lol
Hi, professor. I had a question. Why compared with arr[maxW - curW][ItemNo-1] rather than arr[maxW - curW][ItemNo]? For example, for weight = 4, why we choose 30+0 rather than 30+30?
lol I'm no professor, and I can't answer this (due to time and me not remembering the problem well enough), rapid replying to comments, I love you and am sorry but know I saw this
After multiple videos and almost giving up on it since a year, I finally understood 0/1 Knapsack.
great!
once again thank u for making this video ....very clear explanation....But, you talked too fast at the middle of the video than starting , made me to slow down the voice playback speed to 0.75.....But Before your efforts of yours in making this clear explanation...those are nothing.....I loved your teaching...and decided to watch all your vedios ..
thanks and thanks and I know - this was one of the first 10 videos I ever made and I had no mic or audience
Hello there , On LeetCode there is a question - Target Sum -Find out how many ways to assign symbols ( - + ) to make sum of integers equal to target S. Can this be solved with the same approach as explained in this video ? Will you be able to share your thoughts or put a video for this ? Here is the problem .......You are given a list of non-negative integers, a1, a2, ..., an, and a target, S. Now you have 2 symbols + and -. For each integer, you should choose one from + and - as its new symbol.
I'm not sure I've never done that problem and do not know
I don't think 0-1 by definition means that you cannot split item, it means that you can either take an item or not, as opposed to more than 1 items could be taken for bounded knapsack or unbounded knapsack problem. Correct me if I am wrong. check wikipedia
Yeah, by default you can't use more than one item so that bound is set. Max 1 use per item. 0/1 sets the "middle bound" of sorts saying no splitting...so either use 0 of an item or 1 of an item.
Thank you for your content! I have a question - in the coin chance problem we subtracted the amount by the new coin and moved left to the column but stayed in the same row. Here, we subtract the weight of the item from the weight constraint (moving left) but then go up one row to add to current item's value. I don't quite understand why we are doing this. What's the intuition behind this?
The subproblem weighs 2 choices, if we use the item we can't use it anymore and the weight left changes. If we do not use the item we just don't use it. These 2 subproblem answers are necessary to solve the subproblem we stand at.
@@BackToBackSWE Gotcha thanks for the reply! So if we allows replacements for this problem (unlimited number of each item) it is just like the coin change problem that allows unlimited number of each coin.
@@seungjinkim8860 yeah
Question about the problem, are we assuming there is only one quantity per item? For example, for Item 4, we cannot choose the same item twice at weight 4? It doesn't change the outcome of the answer, but just wanted to understand the problem completely. Thanks in advance!
Yes only 1 of each item & they cannot be split. Imagine this as a real life assortment of choosable objects.
Amazing video explanation! I second the medals and beers, but do pay for backtobackswe.com. The video explanations themselves are worth it and plus we are already getting free content from his youtube channel.
haha - we are going to rewrite the whole site and make it a lot better. The current site is only a trial run from this year's start
Did anyone else have to watch this 3+ times for it to finally click?
These jump cuts are very jarring. Makes it hard to pay attention
yes, this is one of the first videos I ever made
Someone make a "Don't memorize the pattern, think about the sub-problems" wallpaper please.
hahahaha
he looks like marcel from THE ORIGINALS(Netflix) :p
sorta lol, he's black u got that haha
what happens when (not taking the item == prev value + value of row's item) when trying to find out which items where selected?
how do we know if we selected the item or not? I guess we can go either way and there are several different choices yielding the same result.
Yes. Then multiple decomposition paths can be taken (meaning multiple solutions to get the same optimal result).
Great video. The thing that took the longest time to click for me in this problem was in the case where the weight of the item was less than the current amount we are at. Was confused as to why we not only decrement column by weight, but ALSO decrement row by 1, until I realized that items cannot be reused. I was under the impression that we have infinite number of each item. Having only 1 of each item makes this make sense
Yeah, it is all about knowing how the subproblems decompose
Thanks bro! This is exactly what I had a problem understanding. Didn't click until I read that "items cannot be reused".
Amazing man! Especially how you dived deep into the logic of constructing this matrix. Thanks for this and all the other enriching videos!
thanks and sure
But when the new item's value = the combination of old one, how do you track back? When Math.max (a,b) a=b!
Either path can be taken (that's the interpretation)
Hey bro love the way you teach but the sound of the video is somehow difficult to hear because of echo you know what I mean, good video by the way!
yes I didn't have a mic back then and thanks
you just converted an 'NP-hard' problem to 'NP-understandable'...:)
no
problem
This is awesome. Thank you for the detailed explanation. I also used "Grokking Algorithms" to consolidate my knowledge, especially to understand the concept of why we're going up one level (You're on 4max but the item 2 is 3lb, so we have 1 lb spare, (equates to going back three steps and looking at the corresponding value for item 1). Brilliant explanation. You're my hero!
sure and thanks.
Man... The amount of effort that you put in these videos is literally unmatchable, and that has made these difficult-to-grasp concepts very intuitive! A big thanks to you!
nice, thx
Wonderful explanation as always!
I'm just concerned, if accidentally any non programmer listens to this, say from humanities, they think you're planning a revolution to overthrow all the Govts in the world and teaching us a technique to find THE BEST form of Govt! :)
lmoa what
@@BackToBackSWE This is a compliment. I was talking about the passion with which you're trying to explain. Probably this is how Socrates would have explained "what is a better govt than democratic govt", by tabulating and giving all possible options, with the same intensity and passion!
Also this is also how in India every "Constitutional Amendment or major Bills are damned by leftists, liberals and students". They passionately tabulate all the possible (and impossible) options(draw backs) :D.
Every other programmer explains, but you try to convince by appealing to the natural intuition, why are we doing what we are doing.
Thanks a ton for all your videos!
I'm a 10+years experienced s/w engineer in break since a while. It's helping me a lot in brushing up my forgotten algorithm writing skills!
It would have been hard for an old lady like me without your videos ;)
u explain fabulously.. keep making such videos.. they r really helpful to us
ye
The best thing I find about your videos is that you show the whole thought process of solving a problem. I must say you are a lot better than Tushar Roy ! In fact I think you are the best Computer Science teacher on RUclips.
haha, nah Tuschar Roy is the og...the Don might I say.
And I'd agree with the last part :) Wish I had more time
Omg Thank you so much I had been trying to understand this since the last three hours, I finally get this(^__^) Thank you
great to hear
Why did'nt we do "row-1" in the coin change problem when we take that particular coin as we are doing here?
In the coin change problem:
table[row][column]=table[row-1][column]+table[ROW][column-coins[row-1]]
But here it is "row-1" in both cases.
In the recurrence? I think that may be an error
I was wondering the same thing.
The coin change allows you to re-use the same coin infinite times.
Here you can only re-use the same item once, so you can't build off any sums in the current row, only from those above.
Hope that made sense
Why would the time complexity be O(2^n) for the brute force? At every level we can pick n elements. And if each element is 1 dollar then there will be n levels. Shouldnt this be O(n^n)?
Answering my own q: At every element we can either choose or not choose an element so there are 2 choices at each level and n levels therefore 2^n
@@varunrao2931 yes
i dont know who you are.but i'll find you and i'll hug you
thnxx for the content
lol ok
my brother peeping through me tells me you look like willsmith,#you teach great
ok lol
Couldn't see the board cuz you kept moving around. Thanks
sorry.
I enjoyed watching this video. Your energy made it more interesting and I finally understood this concept.
Nice - and great to hear
Very nice explanation. Much better than fake accent "I am Tushar"
thanks and lol
Thank you so much! You just unlocked my brain for DP :-D
ye
How can we show that this method can be influenced by the number of item ?
what do you mean by "show that this method can be influenced"? sorry for misunderstanding
I love the way you teach. You break down the problem and at each point repeat what is happening so it stays in your head. Keep doing what you do.
Thank you for this! I have a b2b phone interview with Google in 3 weeks so I will be checking out most of your videos.
AWESOME!! Tell me how it goes!! Man...this is literally why I do this. Thank you for commenting. Thank you.
@@BackToBackSWE got denied by the hiring committee but I wouldn't have made it to the 7th round without your help. Thank you bro
@@dephc0n1 sure...you were my first ever real comment on the channel...pretty cool :) I remember you.
How did you know the sub problems came from the table?
wym?
Correct me if I am wrong, but for the greedy approach (fractional weight allowed), we should sort them by value/weight ratio, and not just value, as you mentioned.
I am honestly not sure, I'd need to think further on this
You're right man, that's what the greedy approach does. If I remember correctly, the greedy algorithm could have problems and might not reach the optimal value.
There's a very good book which describes the KP formally: Knapsack Problems by Hans Kellerer. Very good book although is too mathematical from my point of view, but reading the specific KP section from the book and watching a great video like this and you're good to go for implementing the algorithm in your favourite programming language.
@@AlejandroRodriguez-qy3gh Dang, someone wrote a whole book on Knapsack Problems. Fascinating.
Your explanations are just awesome!!
good to hear
Please find time and replace algo prof. At universities.
lol - will try
Do the weights have to be sorted before putting them into dp table?
No, they don't
can you please explain why the brute force solution is 2^n
I forgot to be honest - had something to do with 2 choices for each item generating all subsets? It's foggy
This is hands down the BEST explanation of this problem I have ever seen!!! I don't know if it's just me but generally I really struggle wrapping my head around the knapsack problem, I just never fully get i. This makes so much difference, thank you!!!
nice
Are you allowed to take the same item multiple times? If not, how can you prevent that from happening?
Each item only once, and not sure? I don't remember this well enough would have to jog my memory
Back To Back SWE because the method you used does not prevent you from choosing the same item multiple times. Any ideas on a solution?
Hey man, you are a great teacher, just wanted to let you know. Your enthusiasm is infectious, and I will definitely look at more of your material in the future.
Great video!! You look like kofi Kingston btw😅
thanks and not at all
Fantastic articulation and great clarity! Taking the time to go through the entirety of the procedure is a very useful and often necessary part of the teaching process that, sadly, too many people forget, so props for giving that extra effort! Only thing holding you back, in my honest opinion, is that you speak way too quickly. On RUclips, people can pause and take a break (or re-run the video a few times), so it's not as dramatic as if this were a classroom, but too much energy and flow will detract from your otherwise remarkable clarity for people who have a harder time understanding the problem. Remember those people are trying to think while you talk, so they need as much breathing space as you can give them (without falling into the other extreme, of course). I would recommend experimenting with the way the video is cut. The montage is excellent, but it's clear it's arranged to create one massive, expertly flowing, high-speed delivery that may end up defeating itself by virtue of offering the info faster than it can be processed.
Another suggestion would be to avoid correcting mistakes made while recording with white-on-white script. by the time the viewer sees it, then reads it, you run the risk of losing them, in part due to the technical nature of said corrections that inevitably detract from their attention, and to the speed of the delivery.
And I'm not saying this to complain : this is by far one of the best explanations available on the net for this problem! There's clearly an immense amount of work both recording and post-prod that went into this, and you should be bloody proud of it! Thanks a lot for your work!
thanks
If I were a girl I would have definitely kissed you.
ok lol
the white text flashes by too fast
my b
Back To Back SWE no worries man I like your videos, appreciate you making it! Please make more DP videos!
@@CyberMew ye
Great explanation
thanks
Superb! Completely understood from your video.
great
Amazing explanation. Now I can do DP problems. Thanks, dude.
sure
big "Like" before even watching the video, I was happy just to see you making a video about this problem.
thx!
As I thought, I fully understand it now ❤️.
Thank you
i really love the way you teach. It's really inspiring. hope you have a lot of lesson like this i love it and look forward to see your other video soon
thanks
knapsack problem will make you jump! jump!
what
destroyed the gimmic behind DP :p Awesome
ye
it's wonderful man can't explain better
thanks
After watching several videos it finally clicked at this one. Thank you!
nice
This guy is a gem 💎! Cos I see the genuine interest in knowledge sharing 👍🏻
thanks haha, im here
Hi, I am a a bit confused at determining 'which items we picked' step.
So When we are at (4,5)=80 . We checked (3,5)=70. And its indeed clear that we did pick up item 4.
After that, since weight of item 4 is 2 ----> 5-2=3 so we reached cell (3,3).
(3,3)==(2,3) So we did not pick item 3.
Now, though it is clear that (2,3)!=(1,3) and we did pick item 2 BUT for checking the second condition,
Will we check --> val(item2)+val(1,0)cell or will we check val(item2)+(1,1)th cell.
(Although currently that won't change answer, but it might in the future.)
Because upon depicting this process in the video, You sticked to a fixed Zig-Zag pattern(1 cell up 2 cell backwards), but my question is, it can vary depending on the weight of the item(1 cell up, 'x' cell backwards) where 'x' depends on the remaining weight upon choosing a particular item,right?
Or will we stick to the pattern 1 cell up , 2 cell back if the above value is not the same. if yes, why?
ALSO--->
Is this method strictly for non repeating Discrete Knapsack?
Hope I was able to express my confusion properly.
Please let me know.
Ps - Great Teaching !
Thanks!
Can you timestamp that part? There can be multiple decompositions sometimes, but yeah when you trace back as long as you follow the same recurrence logic that we used to build the table you will get a valid decomposition.
"Is this method strictly for Non Repeating Discrete Knapsack?" Probably? Repeating item weights or values wouldn't change how we would approach it since every item gets considered in the subproblem decomposition.
@@BackToBackSWE
I really appreciate you reading all of what I wrote! Thanks.
Its 18:05 - 18:21 in the video, And the gesture is specifically at 18:16 which is confusing me.
Shouldn't you be going 3 cell back?, instead of 2 (as depicted) Because weight is 3 pounds?
@@varungupta7226 I agree with you Varun. I have the same confusion. I think you are right. Please have a look at this Benyam.
P. S. I love your explanations. There are really helpful. I have recommended all my of friends to watch your videos, if they get stuck somewhere and you have a video on the same topic.
very nice i understood just like that !!
thanks
I don''t usually comment, but this video is too amazing not to say anything. Thank you! I finally understood this, and it was actually easier than I thought. You're awesome! Keep going.
ye
great explanation, thank you, bro...
sure
This is amazing, just wanted to say thanks. I've been struggling with understanding other explanations for a while now. This video really helped.
excellent
This is the probably the 4th or 5th explanation of the knapsack problem that I've watched in the past few days. THIS is one where I had the moment of epiphany where I said "OH... I get it." Thank you.
great
Your videos rock man!! By far the clearer explanation of the Knapsack problem I have found in RUclips. Keep doing the good work !!
ye
you explained better than Tushar.
thx
How i can solve this same problem when repetitions are not allowed??
There can be multiple items with the same weight and value and the algorithm will still be the same.
@@BackToBackSWE
Ohh I get it ✨, thank you
Just Brilliant!
I love how you explain things! You don't skip steps and at the same time have great banter as you progress through even simple steps. It allows me to follow along and eventually grasp how the solution needs to be approached. I'm very impressed. Well done!
thanks and thanks