[SQUEAKING] [RUSTLING] [CLICKING] JUSTIN SOLOMON: So
today, we're going to continue in our discussion
of dynamic programming. I actually found this set
of problem session problems to be easier than
the previous one. There's a funny
thing, which is we learned in class about
pseudo polynomial time style dynamic programs. Somehow, that language is
a little bit liberating, in the sense that you're using
parameters that you really shouldn't, when it comes to
the runtime of your algorithm. Well, I suppose we should, in
the sense that it's allowed, if you call your algorithm
pseudo polynomial time. But it somehow makes
it a little easier to formulate your dynamic
programming algorithm, because all the numbers are
staring you right in the face. You don't have to be so
careful about what's fair game and what's not, when you post
your algorithm so long as it's efficient in the values
that you care about. And so today's problem session
has five problems squeezed instead of the usual four. We'll see how far we get,
seeing that I usually talk too much anyway. But I'll try to stay
on schedule here, and we'll see how well we do. Any questions from our students
about dynamic programming before we get started here? AUDIENCE: You can
cut one, if you want. JUSTIN SOLOMON: We can cut one? Oh, I will gladly cut
one if I run out of time. Yeah. OK, so without
further ado, let's get started with coin-crafting,
which is problem 9-1 here. So I suppose it
should be Ceal Naffrey if I were to work through my
splinterism properly here. In any event, we
have a thief who's in desperate need of money,
as with many thieves, or else, of course, they wouldn't
resort to the world of prime. And Ceal Naffrey here
has n identical coins. I've been very
self-conscious about the way that I write the letter I,
ever since Eric pointed it out, and it's only gotten worse. Now it's inconsistent
and hard to read. But in any event, so we
have n identical coins. And of course, these coins
have very distinctive markings. And so we can't possibly
run away with them as is, because if you take
them to your standard jeweler, they'll immediately recognize
that these markings are bad and stolen. And that's not so good. So instead, we can melt these
coins into other objects. So our sneaky thief here has
identified a potential buyer. And the buyer has a
few criteria here. So we have a buyer, and the
buyer has a very strange value system thing, that
apparently, it's easy to take coins and make
them into other things. But in any event, the
buyer, what they care about is not the fact that the
coins are made of gold, but rather that they like
particular objects better than others made
out of said gold. So in particular, they
have a different rate for each object, a different
price they're willing to pay. And so they have a
list of n objects that they're interested in. And each one is associated
with two things of n objects. They have a price-- so the amount that the
buyer is willing to pay for that object, which, again,
is not just the weight in gold, for some reason. There's a value added
tax in this universe. And it takes a different
number of coins to manufacture. Right? So maybe I can make a
golden chocolate fountain, and that takes 10 coins. But I don't want two of those. So if I make that, then
the next thing I have to do is a figurine of Eric and
Jason to put alongside it. And having more
than one of those would also be creepy and weird. So I can only make
one of each object. And of course, my goal
here is I have n coins. By the way, the
fact that there's n coins and n objects for the
buyer doesn't really matter. I mean, it will for the runtime. But you could imagine
this being n and m. So I wouldn't be
too hung up on that. And what you're trying
to do is, of course, maximize your revenue,
subject to the constraints that you have n coins,
and you can't make two objects that are the same. Hopefully, I've captured the
essence of our problem OK. OK, fabulous. So as with all of our dynamic
programming problems in 6006, we have a paradigm for
how to approach them, which has a cute
acronym, which is SRTBOT And I think SRTBOT is a totally
relevant and straightforward approach to this problem here. Yeah, so in particular,
let's give ourselves a bit of notation. So we're going to number
our objects between 1 and n, just for convenience. So we'll say that Pi is
the price of object i. OK. And we'll say that Ki is
what the problem calls the melting number. In other words, if I
want to make object i, this is the amount of
coins that I'd have to melt to make that object. OK? So just a tiny bit of notation. And in general,
so OK, what do we do when we formulate our
dynamic programming problems? Some problems we could
solve that are smaller. And when we compose
them all together, we get the final
solution to our problem. And this particular problem,
involving manufacturing objects out of coins, I think is a
really classic one, when it comes to dynamic programming. This is the sort of
thing where someday, when you're trying to
pay for your tuition by doing these hacker
contests online, this is the sort of thing
that comes up all the time in that universe, right? So in particular, the
two variables here, when I make a new object,
is what object did I make? And how many coins did
I spend when I did that? So those are the two
natural parameters to use, when I solve my
dynamic programming problem. Yeah. And of course, it's going to
be recursive, in the sense that I can either choose
to make object i or not. And it doesn't matter what
order I make my objects in or similarly, what order
I spend my coins in, which is usually a
nice property to have in a dynamic
programming universe. So in particular-- oh,
this thing is my nemesis. This is front. I think this class
is particularly tough for short people, because
it's moving up and down all the time. OK, so given our observation
here, if we're doing SRTBOT, so what's our S again? Let me ask myself that-- Sub-problems. Thank you, Professor Demaine. Then essentially,
what we want to do, based on what I argued
verbally, is maybe define our variable
x i, j the thing we're going to compute, to be
the revenue from using i coins and objects 1 to j. OK. So in other words, I have
i coins left in the bank, and I'm only allowed to
use the first j objects. And we can already see that
this is kind of a sensible way to approach this
dynamic programming problem, in the
sense that there's an obvious recursion here. I choose to make an object. I have fewer coins. And I can sort of imagine there
being a topological order, in the sense that I could
first decide about object 1, and then object 2, and object
3, and so on, or vice versa, depending on whether you're a
prefix or suffix kind of guy-- which I still get backward. But the good news is that
it doesn't really matter. What matters is
formulating the equation. OK, any questions about
our definition of the thing that we're going to
chase after here? Fabulous. Ah-- OK, right. So let's continue SRTBOT. So our next piece
of our puzzle here is the R. I believe R
stands for recursion. This is a new
acronym for me, too. Ope, no, Relate. But it might as
well be Recursion for most of these problems. Right, so the basic relationship
here is that, of course, I can either use object j,
or I can not use object j. And in both of those cases,
If j is the very last guy that I'm going to
consider, that'll be a totally reasonable
recursive rule, right? So in particular,
I have that xi, j essentially can take
one of two values. That's a parenthesis, in
case you're wondering. And of course, you're trying
to maximize your revenue. So let's do that, OK? So we have two
potential options. But we have to be a bit careful. Is there a case where
I can't make object j? Yes, class, there is,
which is the case where I don't have enough coins
left in the bank, right? So I want to make that
really expensive fountain, but I only have one coin. Then I'm out of luck, yeah? So let's do these two cases. So first, if-- oops, I
got my cases backward. That's OK. Right. So let's say that I choose
not to make object j. OK, so what does that mean? So did I spend any coins? No. And moreover, what
is my maximum profit? Well, it's going to be the
same as the maximum profit, using objects 1
through j minus 1, because I didn't use object j. Yeah? So in particular,
that would be x. Let's see, I got it
backward in my notes, so we're going to do
it live, like that. OK? And otherwise, let's say that
I did choose to make object j. Well, what happened? So I did get some
revenue now, right? Man, what on earth did
I write on these notes? So I get the price of
objects j here as my revenue. But I spent some
coins in the process. So I have i minus K sub i,
which is the number of coins. What's that? AUDIENCE: K sub j. JUSTIN SOLOMON: Oh, thank
you-- sorry, K sub j. That's why we should be
consistent with our indices when we write these things down. So I spent K sub j coins
making this thing, object j. And moreover, I can
still choose to make any of the previous objects. So it's still just j minus 1. But I have to be careful,
because I can't always do this. In particular, this had better
be a positive number for n, or at least a
non-negative number. Because if I end up with a
negative number of coins, well, that that's not
a physical universe that I choose to be in. So in particular, what
we need, in some sense, is i minus k minus
j, and k and j to be greater than
or equal to 0. Or equivalently, i is greater
than or equal to K sub j. I think you guys could
all do that one at home. OK, and this is our recursion. Hopefully, I've gotten it
right, because it disagrees with the crazy thing I wrote in
my notes at 1:00 AM yesterday. But I think it's
pretty straightforward. Essentially, either I can
choose to use the last object, or I choose not to. And either one of
those, of course, decrements j, because
that's the index of the object I'm considering. And I either account for the
price, but have to pay in gold, or I don't account
for the price. So implicitly, there's a 0, and
I don't have to pay in gold. OK, good. All right, so let's
continue with SRTBOT. We might, later in
the session, relax going through every
one of these steps, because a lot of the
arguments are similar. But for now, we'll do
one or two carefully. So T, I believe, stands
for Topological order. And here, it's staring
us in the phase. Because notice that
xi, j only depends on x question mark,
comma, j minus 1. [LAUGHING] Right? So of course, on
your problem set, you should write
things more carefully. But the basic point
here is that there's a clear topological
order, just by looking at that second index. Because if you think of all your
x's as variables in a graph, which we've actually
drawn in lecture-- so maybe these are all
the i's and then the j-- so i goes down, and
j goes to the right. Then essentially,
this argument is saying that all the arrows
point left in this graph. I suppose the way I've drawn
my arrows isn't quite accurate. But it actually doesn't matter. The only thing that
matters is that it goes from right to left. But I'm going to erase this,
so you don't remember it. OK. So in general,
just when you want to make your topological
order argument, I think the totally
sensible one is looking at the
indices of recursion and then just trying to find
some number that decreases. Incidentally, if you take a
differential equation course, that's roughly how you prove
that a lot of those things converge, too. So there's a generic math
check that we use a lot. What do you call that
in ODE where have some number that decreases? AUDIENCE: I would call
that potential function. JUSTIN SOLOMON:
Potential function-- that's a perfectly sensible one. It's not Lipschitz. It's some other mathematician. Anyway, OK, so let's
continue SRTBOT. So next we need B,
which is our Base case. So in this case, it's
pretty straightforward. If I don't have any coins,
I can't make any money. I have a t-shirt that
says that at home. And moreover, if I
can't sell anything, I can't make any money. So those are pretty
straightforward cases. So we have that 0
equals x of 0, comma j. Remember, the first index is
the number of coins you have. So this is saying, I
can't make anything. I don't have any coins. And similarly, equals x
i, comma 0 for all i, j. So this is the coins
and the objects. OK. So let's see. I keep writing these
problem sessions too big and then spending
half of it erasing. So let's try and fit
this on one board here. So we're going to do SRTBOT. Then the second--
no, the first O, because there's no O in SRT-- is the original problem
that you want to solve. So of course, you start out
with n objects and n coins. So the original problem
we want to solve is equivalent to
computing x income n. And then finally, we've
got to do our runtime. T stands for runtime,
or Time, I suppose. So first of all, how
many problems are there? Well, there's x i, j. Both I can go from 0 to n. This is a great
way to be off by 1. So there's n plus 1
squared sub problems. And how much work does
each sub-problem do? Well, it does boring work. It's just a formula. Right? So there's order 1
work per sub-problem. So the overall algorithm
takes n squared time. So I promised to do something
in our last problem session. Then I didn't actually do it. So I did think I would spend
just a minute here translating what this SRTBOT thing
would mean in terms of code. Because I think that it's
a little bit implicit here. In particular, I
think this step here, I mean, you will see
that it really clearly is going to give
you an algorithm. But I think it's kind of easy
to, again, just to forget what your code actually looks like. And actually, the
coding problems on these problem sessions
are almost too interesting and can obscure it a little bit. So I thought we'd
do a boring problem and show you that it's really
not so hard to do this. And in fact, we covered two
different strategies in class for how to take
SRTBOT and convert it into a piece of code. Although, they might have zipped
past you in this 2x speed thing that you can do now. So here are two options. One of them is
called memoization. And the other, I don't
know, bottom up, I guess, is a reasonable
phrase to describe. And so I thought
we'd do them both, because they're both easy
for this particular problem. OK. Right. So let's do that. So is this necessary
on your homework? Strictly speaking, no. If you've gone through SRTBOT,
then essentially, everything that happens after
that is boilerplate, in terms of converting these
steps into a piece of code or an algorithm. But I do think, just
for understanding why SRTBOT makes
sense, it's worth thinking about for a minute. So option A here is memoization. Ironically, I've taught and
TA'd algorithms a few times, and I never actually knew
what memoization meant. So I learned something from
Eric's lecture the other day. Which remember, memoization
is the key thing in what, apparently, is a
made-up word, is memo-- if you're back in the day,
and you had a steno pad, and you were writing down stuff. Because that's how you solved
problems, with your slide rule. Then essentially, the idea
is that, if I compute x i, j for some i, j pair, I
shouldn't compute it again. I should just write it
down on my memo pad. Stenoization sounds
better to me. But I suppose we'd have to go
back to the 1940s and fix it. OK, so let's
actually write down. I'm going to write down
pseudocode, that will probably look more like Matlab,
because I'm that kind of guy. So let's say that
I wanted to make a function, which, I guess,
is revenue of i, j, like that. Bah. And this is the thing
I wanted to compute. That's actually going to be
a problem, because we're not going to be able to see. OK, right. So in addition to this, I'm
going to pass in an array x, which is going to
be like my memo pad. This is going to be
terrible coding practice, but easy board coding practice. So if I were in C++, maybe
I'm passing by reference, so that when I edit-- that's a treble
clef, but whatever-- when I edit x, it actually
persists when I recurse. This is terrible
coding practice, and you shouldn't do it. OK? But it's just going
to be because I don't want to write too many
lines on the board here. And maybe we initialize. We have some helper
function to-- let's see, we want
our revenue to be big. Well, actually no. We'll just initialize
it to not a number, so that we know that we
haven't computed it yet. How about that? OK, so what should we do? Well, if we're going to memoize,
the first thing we should do, any time that I call my
revenue function on an i, j pair is check that
I've already computed it. Yeah? So in my goofy, bad board-coding
style here, what could I do? I'd say, well, if I've
already computed it, then this thing won't
equal NaN anymore and won't be not a number. So I can say, OK, if x is
not equal to not a number-- so in other words,
it is a number-- return. And this, I think that this
little line of code here, it gets a little lost. But this, we should
put sparkles around it. This is the magic of
dynamic programming, because I just killed
recursive calls. Even if i and j are 17 and
23, if I already computed it, I'm done, right? I don't have to call
my recursion again. OK. And otherwise, what
am I going to do? Well, otherwise,
I'll maybe call-- do I want to write it all down? I don't want to
write it all down. So otherwise, I'm going
to evaluate, R, where R is this formula over here. Notice that this will require
recursive calls, right? And I'm going to store it in
x i, j and then return x i, j. OK? So basically, the
only difference between what we've seen
in the first 2/3 of 6006 and now is this beautiful line
of code, saying, if I already computed this thing, return it. And this is the memoized
version of our algorithm. I think this is the easiest
one to maybe think about. But actually, from
a runtime analysis, it's a little bit annoying. It's not in the sense that
we convinced ourselves that SRTBOT is OK. But of course, if you're
thinking about your recursion tree, what's happening
is that you're maybe convincing yourself that
this piece can be lopped off in your function calls. So you have to do your
counting carefully. There's a different way to
implement the same thing. So this would be
option B. This is maybe more efficient,
maybe less efficient, depending on your problem. But these are all within
constant factors of each other, for the most part-- not always, but
for the most part. This would be bottom up. And this is the idea
of, rather than just taking our recursive algorithm
that we already know, and then just checking a
table to say, like, OK, did I already do this? And in that case, return it,
in the bottom-up version, I'm going to build up my array
x i, j because so there's no recursion at all. So what would that look like? So in the bottom-up case,
notice that, in some sense, memoization is a
top-down strategy. I would call it on n, comma n. Here, we're going
to start from 0 and work our way
toward n, right? So we'll start
with x 0, j equals x i, 0 equals 0 for all i, j. Obviously, I can do
this with a for loop. And now, well,
remember, if we think about our topological
order, x i, j only depends on
previous j's, right? So it makes sense to have an
outer loop, which is over j. And now, inside of
this outer loop, I can compute anything that
I want in that j column of x. And I'm in good shape, because
I'm building it up one column at a time, right? So in particular, now we
can do our loop over i and then just have x j and
now evaluate our R step in our SRTBOT paradigm. And notice that
that's perfectly fine. Because by the time I
get to computing x i, j, I've already filled
in x i, j minus 1, which is all I needed to
evaluate that formula. So what are the advantages
and disadvantages here? So notice that here, our runtime
is staring you in the phase. Right? We have n squared sub-problems,
order 1 work, and you're done. On the other hand,
there's some possibility. If you were an
old-school AI person, you might be able to do some
pruning on your left-hand side that I can't do over here. Right? So here, I'm
literally evaluating every entry of x i, j. It's not the case for
this particular problem, but maybe x i, j only
depends on x i, j minus 5. This strategy is going to still
build up that whole table. This one, maybe you can
skip over some entries. So in practice, it could help. On the other hand, here, I've
got a bunch of recursive calls I've put on the
stack of my computer, that here, I don't have. So I think, actually, because
of the overhead of recursion, typically, the
strategy on the right is preferred; also, for clarity. But that's a blanket statement
that I shouldn't make. OK, so anyway, I think I've
done this problem to death. Are there any questions here? I just thought I'd fill in for
something I promised last time and didn't actually do. OK, fabulous. So we'll go on to problem 9-2. So this is continuing
from last time in the saga of Tim
the Beaver here. So I forget what Tim the Beaver
was doing in our last problem session. But today, Tim the Beaver
is going to the career fair. And as we all know,
the only real purpose of going to a career fair
is to pick up free stuff. We joke. Actually, you guys should
all go to the career fair. I got my first
job out of college by going to a career
fair and haranguing somebody that picks our
booth, until they let me in. But in any event,
so Tim the Beaver is not interested
in getting a job, but rather just wants swag,
just wants free stuff out of booths at the career fair. OK? So in this particular
problem, there's n booths-- is it booths? It's certainly not boots. I don't know. There are n booths, each
of which has a swag. And in particular,
each swag has a value associated to it,
which is Ci, which is the coolness of object i. It additionally has Wi. This is the weight of object i. And just to make this
problem verbally difficult to communicate, there's a
W-A-I-T associated with each object, which is the time it
takes to wait in line and pick up object i. OK? And Tim the Beaver, if there's
some ridiculously cool object with a little wait time, he
might just keep getting in line and getting more of that object. So unlike Ceal Naffrey
on our first problem, Tim the Beaver is perfectly
happy to have more than one of the same thing. OK. In addition to
this, just to make this problem, in my
opinion, slightly more annoying and point-losing,
Tim the Beaver also takes one minute to
get in line at any booth. So we're just going to
have to remember that when we account for our time ti. OK. So let's continue adding some
more constants to our problem. So each booth has an object
which has coolness, Ci, weight Wi, time ti. Tim is carrying a bag. The bag can hold a
particular weight. b. So this is the max weight
that Tim can hold in his bag at any given time. And finally, Tim
is a greedy beaver, but he also can go home,
or go back to his dam, I suppose, and empty his bag. Right? And so h is the amount of
time to go home and back and empty his bag,
in the meantime. And again, just to
be annoying, don't forget he incurs plus 1
to get in the next line. This is what made my answer
wrong, and I'm bitter. So I'm going to keep
complaining about it. OK. So of course, what
he wants is the max. What would economists call this? I don't know. But for Tim the Beaver, he wants
the max total coolness, or MTC, which is, of course, a
number that we're all trying to optimize, in k minutes. You might remember
those old TV shows, where you get one minute
in a grocery store to empty the shells into
your cart kind of thing. So he wants to do this,
and the computation time that he's reserved for
this is an order nbk. And I know it's a big setup. I tried to document all the
different constants that are in this problem. I don't think I missed any here. OK, fabulous. So incidentally, continuing on-- AUDIENCE: What's k? JUSTIN SOLOMON: k-- k is
the total amount of time that Tim the Beaver has allotted
to do his job fair scavenging. Fabulous. Any other? Cool. OK. Notice that this is going to
be an example of a problem that was not kosher in last
week's problem session, in the sense that k is
included in our runtime. Right? But what is k? It's just a number. k doesn't scale in the size of
your problem in a linear way. So it's not going to matter
for how we solve our problem But it is just a feature
that's worth pointing out. OK. So how do we solve dynamic
programming problems? We use SRTBOT, or a Señor BST,
if you are watching previous iterations of this course. OK, so right, so let's do that. See, I'm trying
to conserve space. This is not going to
end up succeeding. So again, what are the
different sub-problems here? Well, what are the
different things that are limiting Tim the Beaver? What are his constraints? Well, he only has so much time. And this is going to sound
more philosophical in intent, but time always moves
forward for Tim the Beaver. So this is a pretty
good candidate in terms of dynamic programming. Because there's not going to
be some cyclical dependency. Remember that in
dynamic programming, we're all about trying to
identify topological orderings in our sub-problems. And when you see
something like time, not only does it also begin with
t, but it's useful in the sense that time moves forward. There's never a case
where Tim the Beaver purchases a weird
Warp Speed airplane and somehow goes back in time. That doesn't happen in this
particular homework problem and for Tim the
Beaver's for sake, I hope in no homework problems. So this is a long-winded
way of saying that time is a pretty
reasonable constraint to put in our problem, not a
constraint as much as an index, I guess. Moreover, there's
another thing which is limiting Tim
the Beaver, which is the capacity of his bag. Remember, he can
only hold weight b. But this one should give you
the heebie-jeebies a little bit. Because this
problem, as a twist, has allowed the bag
to empty itself out. So it's not true
that somehow, you can come up with sub-problems
where Tim the Beaver is just monotonically decreasing
the weight of his bag. However, if he does choose to
decrease the weight of his bag, he has to spend time doing it. So time continues to move
forward for Tim the Beaver. And that's what's going to
give us our topological order. Yeah? This is a little too
philosophical, I guess. But in some sense, this is
a long-winded way of saying, for our SRTBOT paradigm here, a
totally reasonable thing to do would be to have x i, j-- I don't think, in this class,
we do this definition notation. So just x i, j equals
the max coolness where he has i minutes,
and he has j weight-- let me glance at
my answer to see if it's left in his bag or
weight that he's carrying. Left in his bag-- either one would make
a reasonable problem. I'm just bad at
looking at my notes and seeing them
disagree with the board. So I'm going to try
and stay consistent. OK, so x i, j is the MTC, but
in i minutes with j weight left, rather than in k minutes
with b weight, which is going to be our base problem. OK. Oh no, I did it out of order. Disregard that. We'll get to the b in a minute. OK. OK, so that's our sub-problems. So now let's do the R in SRTBOT. [LAUGHING] I hate this
classroom so much. I'm sorry. So right, so let's say that Tim
the Beaver has i minutes left on the clock. And he has j weight in his bag. He's got a number of
actions that he can take. Yeah? And let's think about what
all those actions are. By the way, there's one thing
that plausibly, Tim the Beaver could do. But he doesn't really need to,
is do nothing now, but then in 10 seconds, do something. You could account for
that in this problem, but there's not a reason, right? He might as well stack
up all his actions and then leave all of his
leftover time at the end. You convince yourself
that that's sort of OK. Tim prefers a
compressed schedule. Right. So he's got a lot of
energy, this Beaver. That's what they say. They're nature's construction
workers, something? OK. All right, so let's think
about all of our options. So for one, Tim the Beaver
could not do a damn thing. He could just sit around
for the rest of time, and that would be
perfectly fine. So a different way of putting
that is that he could give up. How much coolness would
Tim get from giving up? 0. That's right, kids. Giving up makes you zero cool. So OK, so Tim the Beaver
is trying to maximize. He has a lot of
different options. One of them is 0,
meaning he gave up. Right? Notice he could recurse. Like, x-- I guess
what would be-- he could give up for one minute,
and have that be x i minus 1j, or something. I forget if I'm going
to do i plus 1 or i minus 1. i minus 1j. But there's no reason. He can just give up and stop. That would just be
extra recursive cost for no good reason. OK. The next thing
that he could do is he could get in
line for a booth. Right? So first, let's work
out what happens then. So for one, he gets coolness. Let's say that he
gets into booth k. So he gets coolness
Ck when he does that. And now he can recurse. So first, you have to
account for the time. So it takes tk-- that's a t-- time. Or does it? No. Because it takes an
extra minute for him to get in the next line,
or to get in this line. I guess this is better. And moreover, he needs to
account for j minus Wk, like that. Can he always do this? No, he needs to have
this much time remaining, and he needs this
much time in his bag-- or this much weight
remaining in his bag. You guys can work
out the inequality. But since I only
have a foot here, I'll just say, if applicable. This is a great way to lose
points on your homework. But for board-writing, it's OK. So when I say applicable, I mean
these two numbers had better be greater than or equal to 0. OK? And you can do this
for all k, right? So in other words, he can
choose to get into booth k. And Tim has the third option,
which is he can go home. OK? So what happens if he goes home? Does Tim the Beaver get
cooler when he goes home? No, I'm afraid to say. But he does spend time. So how much time does he spend? h-- AUDIENCE: He should
have lost coolness. JUSTIN SOLOMON: What was that? AUDIENCE: He should
have lost coolness. JUSTIN SOLOMON: He should have
lost coolness by going home. No, home is cool, guys,
especially this semester. Stay home. Stay indoors. Right. So he loses home time h and
1 to get in the next line. OK? But he's got a devil's
bargain, of sorts. He loses time, but he
gains bag, bagginess. Weight is the word that I'm
looking for, b, like that. OK? Again, in this case,
remember that he still needs, if i is greater than h. Otherwise, he's in trouble. By the way, I wrote
this in an annoying way. A different way of saying
that is i minus h minus 1 is greater than or
equal to 0, which is really what's applicable
in every recursive call. But this is strict
greater than [INAUDIBLE].. There's another little thing
that caught me up, when I was reading the answer here. OK, and those are all the
options for Tim the Beaver, yeah? Notice that every
one of our options either gives up completely
or decreases time. So we have our
topological order, which is, again,
the arrow of time always continues
to move forward. I'm going to prove that
rigorously by putting a check mark next to the letter T.
Again, on your homework, you should write
out your answers. What is our base case for Tim? Well, how much coolness do
you get, if you have no time? 0 coolness. That's how much. So we have this expression,
x0j equals 0 for all j. Incidentally, although
it's perfectly fine to have this be your base
case, actually, in some sense, I didn't need it, because
Tim the Beaver always had the option of giving up. So you could, I guess,
in this problem, have no base case,
if you really wanted. It would be OK,
but kind of weird. OK. And what's our original problem? Well, he starts out with
time k and weight capacity b in his bag. So it's x k, b. And then finally, we need
to do our runtime analysis. So how many
sub-problems are there? Well, again, a
sub-problem is basically just the number of indexes for
most of our dynamic programming problems. Right? So the first index is time. The second one is bag size. This is always between 0 and b. This is always between 0 and k. So there's order
kb sub-problems. How much time does
each sub-problem take? Well, notice that I
have to loop over all of my different options k here. So I incur-- oh, I'm noticing
k is abused in our answer to this problem. We should use k only once. OK, so here's where
I made a mistake. And I believe it's in
the written solution, but I'm not going to take now. There's k, which is the total
time that Tim the Beaver has, and there's the k that
I use as an index here. And those are not the same. I guess I can make
this k bar really fast. There you go, problem solved. And I just noticed that,
because I was doing my runtime. And it's not order k. It's the loop over
all the k bars. How many k bars are there? Well, these are all
the different booths, and those are n of those. So this is order n
time per sub-problem, which gives me a total
runtime of order kbn, which, I believe-- oh-oh, our desired
runtime was order nbk. But I think we can convince
ourselves that indeed, those are the same thing. OK, so my apologies for a slight
overloaded character here. But honestly, it's one of those
things, if you read the answer, you probably
wouldn't even notice. But now that I'm saying
it out loud, I am. OK. And that solves Tim the
Beaver's maximization problem. He's a very cool Beaver. Any questions about this one? Notice that both
of these problems are very similar in nature. I basically just
wrote sub-problems indexed by every possible
thing and then enumerated every possible solution. I think this is
totally sensible. Again, I remember I had a
math Professor in college that always would use this phrase-- it's important
not to think here. And I think this
is absolutely true for these dynamic
programming problems, that somehow they look a lot
more complicated than they are. Fabulous. So problem 3, protein parsing. Ah, yeah, so this one also got
me tripped up for a minute. Because the runtime
they want is not the runtime of the
obvious solution, but it kind of, sort of is,
after a little bit of fixing. OK. So Professor Leric
Ander has a laboratory. And that laboratory
processes DNA. Ta-da. OK, so let me go
to my notes here, because I think
they're easier to read. So a strand of DNA,
as we all know, because we're MIT students,
is equal to basically a strand of characters that are ACTG. If you ask me to name
what those stood for, I could make a stab
at one or two of them. But I'm a failure of-- I did not have the GIR,
because I went to Stanford. And this is why I'm apparently
a poorly educated person, according to a person
in a faculty meeting. But in any event, so we
have a strand of DNA. It's basically a long
string of characters that are one of four options. I'm told that there's sometimes
a fifth and a sixth option, but not too often. In this problem, there's not. And moreover, so a
strand can be cut up. So I have this big,
long strand, and I'm looking for certain markers. In particular, I have
a list P of markers, which are really a sequence
of less than or equal to k nucleotides. By the way, this really is
something that people do. String searching really is
applicable to processing these DNA strands. Obviously, . I think, in practice,
these techniques have to be a lot more
resilient to error. But really, a lot of these
algorithms we're covering are not all that
far off from how people process these giant data
sets, which is pretty cool, I think. OK, so what are we going to do? We have a string. And then we're going
to make a division. So we'll call our string
S and our division D, which kind of
looks like d1 to dm, which are substrings
that concatenate to make the full guy. So if S is our input
string, then a division D is just like chopping up
S into little substrings, each of which we can give
a name little d here. So big D is the full division. Little d is all
the little pieces. There's that old song about
going through the big D in [INAUDIBLE], Dallas. I think it's for divorce. Right, OK, so the
value of a division is the number of strands. So strands are these little d's
here, that are in our list P. OK? So given S and P, what
we want is the max value. And the runtime
that we're budgeted is kind of a weird runtime. And this should make
us a little suspicious. So the max value is big O of-- I think I wrote it slightly
differently in the problem, but whatever-- distributed
to k, like that. So it's k mod P plus
k squared. mod S. So we have two terms
here, which somehow smells funny in dynamic programming. OK. So what are we to do? Well, what I did is I
ignored our desired runtime, came up with a dynamic
program, and noticed that it was a little
wrong, and then fixed it. By wrong, I mean it was correct. It just wasn't fast enough. OK, so let's do version 1 here-- 1.0, that makes it more
accurate, or more precise. I always confuse those two. So here's a thing that is
going to be a little bit funny. Because it's going
to look like we're going to have an easy
computational problem. But then it's going to turn out
that it's actually too slow. So in particular,
in our S in SRTBOT, what we could do
is say, xi is going to be the max value of a suffix. If you're wondering, I
don't know the difference between prefix and suffix. But I wrote the word
suffix in my notes and checked it at home. Si, comma, colon-- see, it's
suffix, because it starts at i, and it goes to the end. AUDIENCE: [INAUDIBLE] JUSTIN SOLOMON: Huh? AUDIENCE: No comma. JUSTIN SOLOMON: In Matlab,
it would be a comma. And that's a colon,
and that's an i. OK. [LAUGHING] Thanks. I'm inventing my own programming
language on the board as we go today. All right, so and notice
this is a reasonable set of sub-problems, right? Because obviously, if I lop
off some piece of my string, then the max value
I can get is just the value of whatever remains. So spiritually,
this somehow feels like the right
dynamic programming. And indeed, we'll
see that it is. But it just requires
a little bit of a list that could be
to run on the right time. OK? So let's do a recursive call. And this is actually
straightforward. At least, the recursive
call that you want to make is straightforward. And then we'll see that there
is an equivalent formula, which is the one you'll see
in the solution, which looks more complicated. But is the same thing. So I'm going to write
it as pseudocode for our recursive call, rather
than as one giant formula, because I think it's
easier to follow. Not pseudocode-- I
know that's frowned upon in this class, a
description of a set of steps for obtaining a recursive
call, rather than a formula. So in particular, we're going
to initialize xi to be 0. We want to do a
maximization problem. So initializing a variable to
0 is a sensible thing to do. And remember, what
can we do here? So we're looking at a suffix. I could go down my list P of
all the different markers, see if any of the matches the
first couple of characters of my string. If it does, I get
some value, and then I hop onto the next thing. Does that make sense? Oops, I'm realizing I have
a slight mistake here. Rather than initializing
to 0, [LAUGHING] I actually have one
additional option that I forgot to account for. Because I could just
not use this character. I could put it in its
own little snippet and get no value from it. So maybe initially, I make
a recursive call like that. It would be OK to
initialize it to 0 and then do this-- but whatever. Notice we're already
seeing the t in our SRTBOT start to stick out at us. We're going to only depend
on bigger indices i. OK. But in addition to this,
this isn't enough, right? This would obviously just
recurse the end of our list and do nothing. We get value if we can find a
substring that's in our list. So what we could do is,
for each marker in P-- remember P is the list of
things that we're looking for-- OK, what could I do? If the marker matches
S starting at i-- I'm going to just not even
attempt to do Python-- starting at i and ending at
the length of the marker, well, what could I do? I could get $1 by
matching that object. And then I have to hop forward
the length of the string in my recursive call, right? So well, I could do that,
or I could not do that. And I want to maximize, right? So I can keep my old value,
or I could get one point by using this as
my match, plus xi plus the length of the marker. OK? This is actually, in
my mind, the simplest possible dynamic program
you could come up with. This is actually a totally
fine dynamic program. We'll just see that the
runtime isn't good enough. OK. So does everybody
agree this is a way I could solve this
problem, and it would give me a correct answer? I'll do the TBOT. AUDIENCE: [INAUDIBLE] JUSTIN SOLOMON: Yeah, that's
what we're going to do. But we're going to maybe
skip some parts of SRTBOT. So in particular, what's
the topological order? Notice that I always look to the
right, when I make a recursive call. What's my base case? Well, in this case, it's
just a x of 0, I guess, because I'm looking forward. Oh, I'm sorry. My base case is x of the whole
length of the string, which is going to return 0, right? Because if I have no string, I
can't get any value out of it. The original is x of 0, meaning
that I want the whole string. And let's actually do
the runtime analysis, as Jason suggests,
because that's, of course, the relevant computation here. So this is T2, because it's
the second T in SRTBOT. OK, so how many
sub-problems are there? Well, I mean, it
almost looks like we should have a fast algorithm. Because our sub-problems are
only indexed by i, right? So there's one sub-problem
for basically each character in the string. So there's mod S
sub-problems total. But how much time does it
take for each sub-problem? , Well let's be careful. So there's a for loop
over all the markers in P. So I know that I incur at least
mod P work in my sub-problem. Is that all the
work that I incur? It kind of looks like it,
because there's only one for loop, and there's an if. But how do I check
if strings are equal? Well, I have to iterate over
the length of the string. So I incur a second
cost, which is checking if two strings are equal. We know that our
strings are at most-- well, I didn't
actually write it down. But the problem tells
us that our strings are at most length k. So I incur another
factor of k in every one of my sub-problems. And that implies that
our whole algorithm, that I've outlined for you all
above, takes mod S k squared-- oh, I'm sorry. That was a lie. If I actually multiply these
things together, what I get is-- sorry. [LAUGHING] I need more sleep-- S P k. And that is against the rules. So that's frowny face, yeah. Because in particular, I took
two big numbers, in some sense, and multiplied them together,
and that's not good. So what is a person to do? I tried to solve my problem. I came up with-- by the
way, from a partial credit perspective, I think
you'd be doing OK if you got to this point. OK, not great, but OK. But of course, the
problem is asking you to solve this is
this funny runtime, which is kP, plus k squared S. When I see a sum like this-- and remember that there
are two problems to solve. There are two strategies
for solving problems in algorithms class. There's one which is useful
in your everyday lives, which is to devise algorithms. There's a second, which is
to psychologically diagnose your instructors. And I think that second strategy
is actually pretty effective here. I see two terms. Most of our dynamic
programming things involve filling in a
table, where you would expect there to be a product. So in general, I would squint at
this and think, like, hm, maybe I have to do some
pre-computation. Yeah? In particular, we've got to
do a lot of string-matching in our problem, and maybe we
can make that more efficient. Yeah? That's sort of the
main question here. So this thing is too slow,
and we're trying to fix it. The way I'm going
to try and fix it is to say, like, OK, well,
I have mod S sub-problems. If I look at these two
terms, how much work can I actually do in my
sub-problem, something that looks like k squared,
maybe plus this amount of pre-computation. See what I did there? OK. So let's do that. So in particular,
here are the types of queries that I'm
going to have to make. There's a bunch of
times in my code when-- here's a number that-- [HICCUPS] yikes,
I'm falling apart. That's what I get for sprinting
across campus to get here. I'm going to find
a number m i, j. And it's going to be 1
if the substring S i-- oh, man-- to j is in my list
of markers P, and 0 otherwise. By the way, why is this enough? Notice that I'm not
answering, which marker? But the problem doesn't
really care, right? This problem just checks
if there is a marker. And if so, then
I use that, yeah. So if I have this
thing, that's going to somehow make this
for loop a heck of a lot easier, because now I don't
have to do string-matching. We'll return to
that in a minute. OK. So my question is, how
can I compute this thing? And by the way,
notice that I know how to compute this when the
difference between i and j is bigger than k, because I
know that all of my markers have length k or less. That's going to be
important, because even though m is doubly indexed, I
don't actually need to do that. In fact, I could even
store it using less memory than that, if I
wanted to, by just storing that diagonal block. OK, right. So the other thing,
which I think we saw in a previous
problem session, as well, is that when we do a
lot of string-matching, it often pays to put our
strings into a hash table, so that they're easier
to look up later. Does this string exist
in this thing or not? Rather than matching every
character every single time. So maybe we do
that, just for fun. And in general, when you see
a string-matching problem-- and you have a list
of strings, I'd suggest thinking
about hash tables, just for fun and profit. So in step one here, maybe
I put all the strings in P into a hash. OK? How much time does that take? Well, I have to process
every string, find its code, which is going to
take more of k time. There's mod P of them,
so this is k mod P time. Notice that that conveniently
agrees with our first term here. So we feel like, aha,
we're in good shape. We're making progress here. And now, maybe I want to fill
in this m, i, j objects here. How could I do that? Well, for one, I'm certainly
going to iterate over all possible i's. OK? So let's do that. So we're going to do 2. By the way, I'm using 1's
and 2's and a's and b's and the whatever. These are just ways to
denote steps of things. [LAUGHING] OK, so
let's say that I just want to fill in m using
a brain-dead algorithm. So I could go from 1 to the
size of my strings for i. Careful. Now I can't incur an S squared. But I know that my strings
are always at mostly k. So I could do for j equals
i plus 1 to i plus k. So this for loop actually
incurs k time, not mod S time. And now I can do two things. I can find the hash of
the string from i to j. This is going to
take order k time. And I can search for it-- ah, no-- to see if it's
actually in our hash table of P. And if it is, then
I set m equal to 1. Otherwise, I set m equal to 0. And these are constant
time operations, at least in expectation. So how much cost to it? And so this, you can convince
yourself fills in that array m. How much time does it take? Well, I have a loop to the size
of S. I have a loop of size k. I have a second loop of size
k here to compute the hash. So this whole thing is going
to take order k squared mod S time, like that. Now, this conveniently--
there's chalk on the floor. And this is the second
term in our runtime. So this is kosher. We can fill in m. And that's a convenient
object to have around. So the only thing
that remains is to revise our R from above, to
make use of the m that we have. And that's pretty
straightforward. So the trick is to not
lean against this thing and to actually hit
the stop button. I'm learning. So now I suppose we had T2 for
the second T. So for revised R, we should have R prime. [LAUGHING] AUDIENCE: [INAUDIBLE] JUSTIN SOLOMON: What was that? AUDIENCE: The deriviative. JUSTIN SOLOMON: That's right. The derivative-- yeah, we
could do Christoffel symbols, like i, j prime,
and semicolon k. Take 6838 if you want to learn
what Christoffel symbols are. Right, so now, what is
my recursive call for xi? Well, I want to maximize. Well, what can I do? I can check every
possible length of a string that
could be in P, check if it is, using my array m,
and get that amount of profit. So in particular, I get m. By the way, I keep using
the word profit here. I am essentially using
that to mean increment in every single one
of our problems here. I like to think of our
problems as maximizing profit, because I'm a greedy professor. So this is m, i j, which would
be 1 if I found a string there and 0 if I'm not, plus xi plus
j, to account for the length here, where j is in 1 to-- well, either the
length of the string, or I get to the end of-- either the maximum
length of a string in P, or I get to the end of
my array, like that. OK. And this is our
new recursive call. The one thing we
should double-check is, what is the runtime for
actually filling in x now? Well, there's still
the mod S sub-problems. And now how long does it take? Well, now I just have
one loop over k things. This is mod S times k. It's actually less than
any of the terms that's in our runtime. And so this is fine. This actually is kind
of a funny example, where the dynamic programming
part of our algorithm, once we've done all this
cute pre-computation, is actually insignificant,
compared to all the pre-computation that we had
to do in our final runtime-- sneaky. All right, any questions
about protein folding, or whatever it is
that we just did? OK. So as usual, I'm
talking too much. AUDIENCE: [INAUDIBLE]? JUSTIN SOLOMON: Yeah, which
one would you prefer to cut? AUDIENCE: [INAUDIBLE] JUSTIN SOLOMON: I have
very few preferences. OK, so one of your problems-- I would take a vote,
but with our audience, there's is a high probability
of a split on jury here. Right. So there's two
remaining problems on the problem session. As usual, your instructor-- AUDIENCE: [INAUDIBLE] JUSTIN SOLOMON: You
can leave it there. This is another
problem to learn about. As usual, I've talked too much
and haven't got to the end. I get the impression that
in 6006, this egg drop thing is a bit of a tradition anyway. So maybe we'll cover
that problem really fast. Do they do that in
section, some variation? AUDIENCE: No. JUSTIN SOLOMON: Not this time-- even better. OK, so yes, so maybe we'll
do this egg-drop thing, mostly because the
other one, I think, takes a lot of verbal setup. The other one is-- I would say, from a dynamic
programming perspective, maybe not super exciting. But from an interesting
problem perspective, it's kind of cool
to think about. So I'd encourage you
to leave it in there, and you guys can
read it at home. From a coding perspective,
it's also kind of fun. I notice the solution
didn't do what I would do, which would be to
use the bits in the-- assume that something
wasn't too tall, use the bits in an integer to
store your binary variables. But that's an old hack. That's like this old
hack for computing the square root of a
number, that's apparently in the code for the Doom
video game, which involves bit-shifting, and it happens
to agree with square root, for some magic reason, that
numerical analysts really hate. OK, so let's do lazy
egg drop instead. So that's problem 4. OK. So we're in a building. Our building has n
floors and k eggs. I guess it's debatable
whether the building has eggs or the residents--
but in any event, have some set of eggs. And maybe I'm in
this data center or some other weird building. So I don't have heights of
floors that are isotropic, but rather, each floor
has a different height, which could vary. So it's height of floor, or i. I really want to write
flour, but I digress. And we're going to assume that
our list is already sorted. So in other words,
the fifth floor is taller than the
fourth floor, and we don't have to spend n
log-in time doing that. OK. Right. So apparently, in
our problem, we have an egg with a mysterious
mechanical property that we are trying to recover. And all eggs, as we
know, are identical. So the only difference between
eggs is chicken, versus goose, versus-- I'm struggling to think of a
third category of poultry-- turkey. Thank you. But assuming that I
got all of my eggs at the same Stop-N-Shop,
and they all come from the same
species, then they have roughly the same
mechanical properties. Actually, probably
the better setup is that floors are
very far apart relative to the size of an egg. And if I get high
enough, my egg, when I drop them on the
ground, like that, breaks. Didn't break. But it could have broken. And of course, if I drop it
from an even higher height, my egg still is going to break. However, if I have a very
low floor-- apparently, a very low floor. Maybe this is a house for mice. And I drop my egg, it
actually stays intact. And the question, as all
good scientists want to know, is, what is the highest floor
in my building from which I can drop an egg and
have it remain intact? And the question is
kind of a funny one. It's sort of like experimental
design, in some sense. It's not asking, given this
and a list of experiments, try and figure, infer,
something about the eggs. But rather, it's
saying, if I carefully design a sequence of floors
to drop my eggs from, from which upon I
drop my eggs, then what is the maximum
number of experiments I need to do to triangulate
in on that floor, that critical floor,
above which my eggs break? So what I'm given are
the heights of the floors and a bunch of eggs. In some sense,
the budget of eggs doesn't matter more than just
putting a cap on the size of our problem, in some sense. What really matters is I'd
like to use fewer than k eggs to determine that. Because of course,
the remaining ones I'm going to use
to make an omelet. But notice that
I can be a little sneaky in my
experimental design, that what happens if I
drop my egg from a really low floor in my building? Well, it remains intact. So I can schlep down the stairs. I can pick up my egg, and I can
use it for my next experiment. And I have not paid an egg. Yeah? So the first question
you might ask is, like, well, why the
heck wouldn't I just start on the first
floor, drop the egg. If it's not broken,
go on to the next one and then drop the
egg, and so on? That would be the most
egg-efficient plan. And indeed that is the
case, because you'll only break at most one egg. But you're schlepping
up and down the stairs a bunch of times when
you solve that, right? Every single time, you've got to
go retrieve that unbroken egg. You've got to run
down the stairs, pick the thing up, and then
run up to the next floor. And maybe in your
optimization problem, rather than trying to
minimize the number of eggs that you break, you're trying
to minimize the expense on your quads. And so instead, you've skipped
your leg day, or whatever, and the thing that
you're trying to minimize is the sum over the heights of
the drops in your experiments. So you're trying to determine
the mechanical property of your egg by designing
an experiment, sort of a procedure, that
minimizes the number of times that you need to drop eggs. Because every time
you do, you've got to run all the way
back down the stairs, and go look at the pavement,
and see if the egg broke or not. That's a lot of work. OK. This is different from the
classic egg-drop 6006 problem, which I encourage you
guys to go seek out, in previous iterations
of this course. Let me see. And so the question is, what is
the minimum number of egg drops you need to do to ascertain
that for any type of egg-- so I give you a
mystery basket of eggs, and you have to design
the experimental procedure and bound the number of
this particular value here, given a budget of k eggs. OK. And the amount of time that
we have to do that is order n cubed k. Apparently, our
building, we have lots of eggs and not
very many floors. OK. Does our setup make
some sense here? We're just trying to avoid
running up and down the stairs. That's the main takeaway. OK. So what are we going to do? SRTBOT, because that's all
we know how to do, yeah? And in particular, we're going
to make one observation, which is kind of handy. If I drop a floor-- ooh, if I drop an
egg from a floor, in this deterministic universe,
where egg mechanics are very predictable,
there's only one of two things that can happen. Either the egg
broke or it didn't, while I run into
the board again. So let's think about
our experiment. Remember, at the end
of the day, we're trying to figure out the
tallest floor in my building from which I can
safely drop an egg. So if I think about bracketing
that height of that floor, for one thing, do I
ever need a bracket that's not a continuous or
a connected set of numbers? The answer is no, right? It should never be the
case that, like, oh, I think that my eggs
could be on floors one-- only the prime number of floors
in my building, or something. That really makes
no sense, right? Because if I convince myself
my egg breaks at four or five, then obviously, floors six
through n, my egg also breaks. And so I always can just keep
narrowing down some interval. Right? So in particular,
here's a clever S in my SRTBOT, which
is to say that I'm going to say that x, i, j,
e is equal to the minimum-- by the way, I'm writing this
as minimum total height. So this is the minimum total
times I've got to run down the stairs and check my eggs,
or total height that I run down the stair-- the number
of stairs I run down, assuming my stairs
are 1 foot tall-- where I have e eggs left. Notice the way that we've
written the problem this time, I might as well use
all of my k eggs. That doesn't cost me anything. What costs me is running
up and down the stairs. And that I have floors i
through j inclusive to check. So in other words, if I'm
on a floor below floor i, I've convinced myself
my egg won't break. But if I'm on a a
floor above floor j, I'm convinced my egg will break. OK? So what do I do? Well, remember that this is an
experimental design problem. I can drop my egg from any
floor f, which is in the range i to j. And of course, there's
never a reason for me to drop an egg from a
floor below i or above j, because we already know
what happens in that case. OK? So what happens when we do that? Well, if I drop
it from floor f, I have to pay, in terms of
my cost function, right? Because to pay the height of f,
I've got run down the stairs. OK? But in exchange for that,
I learn a little bit about my egg problem. I either get an upper
or a lower bound of f, depending on whether
the egg broke. OK? So let's formalize
that mathematically. So in particular,
we have x i, j, e. Well, what do I get
to control in my life? And what do I have to deal with? Well, what I have to
deal with is the fact that I don't know what's
going to happen to the egg. It might break. It might not. Right? And the egg might be
an adversarial egg-- it wants you to run up
and down the stairs. And I have to account for that. But i and the egg's
adversary need to choose what floor
I drop it from. So remember, we saw an example. I forget what, from class,
where there was a game. One guy was trying to minimize. The other was
trying to maximize. In some sense, the egg is trying
to maximize the amount of work you have to do, running up
and down the stairs to do your experiment. A better way to put
it is that we're trying to upper bound
the amount of work in your experimental procedure. And I'm trying to
design a procedure that minimizes my work. So let's say that
I'm the player. So I want to minimize. And the decision that I get to
make, the control that I have, is what? Well, it's what floor I choose. So let's say I choose floor f. Well, I have to go
down the stairs. So that takes me hf. This was going up the
stairs, is probably what incurs the hf
going down is nothing. But I digress. But now I still am not done. I've narrowed it down into
one of two cases, right? Either f is my new lower
bound or my new upper bound. And I have to account for
both of those in my recursion and actually, the max of
those two, in the sense that I need, in
every possible case, that my egg drop
experiment narrows down my floor to a width of 0. So in particular, this
is a mini-max problem. There's a max inside
of of a min here. So either the egg broke in
my experiment or it didn't. Right? So if it did broke, then,
well, what happened? Well, let's see here. If the egg broke, then I got
an upper bound for my floor. So my lower bound
remains the same. It's i. My upper bound is f minus 1,
because it broke on floor f. Well, what happens to
eggs when they break? I can't drop them
from floors again. So I lost an egg. OK? So this is my egg broke. OK? Or my egg didn't break. So in that case, well,
if my egg did break, now I have a lower bound. So I'm only unclear
about floors f plus 1. But the upper bound
didn't change. It's still j. And how many eggs do I have? Well, my egg didn't
break, so I can run down the stairs, which is
going to be tiring, I've accounted for that here. But at least I
can re-use my egg. So I didn't lose anything. OK? And I get to choose. So notice-- oops--
so do you guys see why there's a max here? Essentially, I have to account
for every possible scenario when I'm designing my
experimental procedure. But I get to
minimize, in the sense that I can choose what
floor at every step. So in particular, my f here is
in the range i to j, like that. OK, and now we have
our recursive formula for our egg drop that
minimizes total height. So now let's finish off
SRTBOT in four minutes. It's actually not too hard. So I think we'll
actually make it for once by removing
20% of the problems I was supposed to cover. All right. So first of all, what's
our topological order? So this one seems
kind of annoying. Because I think
usually, we think of spending stuff in a lot
of these dynamic programming problems. But do we actually spend an egg? Not necessarily, right? Because in this recursive
call, the number of eggs I had remained the same. So maybe that's not actually
a great way to establish a topological order. But instead, what do we know? What is in science the
purpose of an experiment? It's to improve our
understanding of the world. In this case, our
world consists only of eggs and floors of buildings. And in particular,
once I drop that egg, I learn something
about my building. And I narrowed down
the range of floors that are uncertain for me. So in particular,
I know that x i, j, e only depends on x, I guess,
i prime, j prime, e prime, with what? Well, my sub-problems, I always
have a smaller range of flaws than I did before. So in particular,
j prime minus i prime is going to be smaller
strictly than j minus i. And that'll give me
my topological order. Cool. And so that's actually, I think,
the sort of annoying part, other than working out this
mini-max expression here. The remaining things
are not so hard. So what are our base cases? Well, let's say I
have 0 eggs left. But I still have a set
of uncertain floors. That's bad news. Yeah? Yeah, so that
should be infinity. And the reason is, of course,
I'm going to take the min here. Right? And so obviously, I should
never choose infinity as a min. So in other words, I should
never choose an option for a floor that
could possibly lead me to an uncertain scenario,
when I run out of eggs, yeah? In addition to that, there's
another base case here. This is I've got no eggs left,
but some floors to check, is the second one, i minus 1e. So in this case, I've got e
eggs left, and I'm done, right? I've narrowed it
down to the bounds. Incidentally, the way I wrote
it in terms of inclusive, versus exclusive, might
be a little fishy. Over here, you guys
shouldn't be off by 1, like your instructor often is. But in any event,
here, you're 0, right? There's no more
floors left to check. You've narrowed it
down to a range of 1. Again, something
that I'm out of time so I'm not going
to check carefully, is if my bounds are inclusive,
should that be i, j minus 1-- i minus 1 or just i, i? But I think you guys
are all smart enough to work that out at home,. Which would my original
case be, well, now I have all the floors to
check, and all my eggs in my metaphorical basket here. So I have floors 1 to n here. And the problem tells me I
have k eggs when I start. And then finally, I need
to do my sub-problems here. I think you can actually
simplify the argument that's written down a tiny bit. And just again, look
at your sub-problems. They're indexed
by three numbers, I'm going to do a really
conservative estimate. I think the problem actually
works out a better estimate, but then asymptotically,
it's the same. What's our bound on the
first and second index? Well, they're both
just the index of floors, which
go between 0 and n. Yeah? Obviously, you could do better
than that, because the lower floor is always less than
the upper floor, which is what the problem accounts for. But if I'm being lazy,
then, well, there's n squared sub-problems to
account for the two floors. And the third
index is your eggs, which you have at most k of. OK, how much work do we
have per sub-problem? Well, let's see here. There's a for loop over f. f Is over floors. Again, if I'm going to
be really conservative, well, there's at most n
floors total in my building. And so that leads us to
a runtime and n cubed k, which is what we wanted,
at the end of the day. OK? This should be a big O here,
because I think technically, this is n plus 1, to
account for floor zero. OK. And that solves our
egg-drop experiment. I think this is a nice one. And I think, in
my mind, actually, in terms of dynamic
programming, this is one of the harder
things to get right, which are these mini-max games. I'd have to think
about it, which in my negative two minutes, I'm
not going to have time to do. I think in lecture, the way
that we solve minimize problem was we separated out
the min and the max, and we thought of there
being two dynamic programming problems that are
interacting with each other. You could probably write this
one in that form, as well, I guess, just by pulling
this term out and thinking of it as a different array. But this form is
perfectly fine, too. Either one's all right. But in my mind, these
are the hardest things to get right in
dynamic programming. So I would choose whichever
one jives in your brain. So in your thing, should we
choose to leave it alone? There is a fifth problem
here, which, as usual, I haven't managed to get to,
where you're building walls by placing tiles. This is an interesting
one, because your runtime is exponential. But the problem tells
you that that's allowed. But there's some exponential
things which are OK and some that are not. Essentially, what you
don't want is the product of two giant exponentials. You'd like to just
get it down to one. AUDIENCE: It's
basically saying it's going to be polynomial
[INAUDIBLE] small [INAUDIBLE].. JUSTIN SOLOMON: That's right. Or it's polynomial in
everything except for the things it's exponential in. And moreover, the things it's
exponential in are small. And the problem says that. So I encourage you
guys to take a look. Because it really does take
some time to logic through it. But the setup for
that problem is, I think, longer than my
glacially slow board-writing can handle. But with that, we'll
call it for the day.