```Bowen Yu
Programming Practice Midterm, 7/30/2013
There is a ferry that has two sides (port/starboard). Both
sides are L-unit long (L<=10000). You are given a
sequence of cars to be loaded onto the ferry. Each car
has an integer length in [100, 10000].
You can decide to which side of the ferry each car is
loaded. But you have to load them in the input order and
stop as soon as one car cannot be loaded to either side.
do).
2
L = 5000, Cars = {2500, 3000, 1000, 1000, 1500, 700, 800}
6
port
starboard
starboard
starboard
port
port
(2500)
(3000)
(1000)
(1000)
(1500)
(700)
Let’s verify:
port = 2500 + 1500 + 700 = 4700 < 5000 (4700 + 800 > 5000)
starboard = 3000 + 1000 + 1000 = 5000 (5000 + 800 > 5000)3
You may forget about the ferry-car story and switch to a
A simpler model:
You are given some objects one by one. Pack as many of
them as you can into two equal-size containers, so that
the volume sum of the objects in either container does
not exceed the capacity of the container.
4
Values are all integers, i.e. discrete.
Good for memo!
The size of the container is up to 10000.
Small!
The size of one object is [100, 10000].
Small!
Unfortunately, the number of objects is not given (at first glance
there is no bound).
Can we determine its range?
YES!
The container is at most 10000 large, and each object is at least
100, so we can pack at most 200 objects (don’t forget you have
2 containers!).
5
Enumerating decisions for 200 objects is clearly TLE. So
There is no greedy strategy that makes sense. (Easy to
show yourself some counterexamples)
See that the input are integers, and limited in range (up
to 10000). Also the objects are given in an order that we
can not change (forward property).
This recalls us directly to DP.
6
To use DP, we need to first choose the states we will maintain.
Candidate 1: memorize our decision for each object
Not feasible, that is 2N
Think…
How does our previous choices affect the current decision?
Only the volume used matters!
We just need to know whether we can pack the previous
objects into the two containers, so that we occupy V1 unit of
space in container 1, and V2 unit of space in container 2
7
Candidate 2: memorize whether we can pack the previous
objects (objects that appear before the i-th object we’re
currently considering) so that the volume used are (V1, V2)
respectively for the 2 containers. So the state is (i, V1, V2) =
{true, false}.
Not feasible :(
This would require [200][10000][10000] for all the states,
which is clearly MLE
8
We need to reduce the space requirement for our DP.
Think…
Can we drop one variable, and recover it from the others?
YES!
Suppose we know a valid state (i, V1, V2).
Note that we also know the sum of volumes of objects we’ve
already packed, say Vsum. Then, V2 = Vsum – V1.
9
Candidate 3: memorize whether we can pack the previous
objects (before the i-th object we’re currently considering) so
that the volume used are (V1, V2). But the state is (i, V1)
instead of (i, V1, V2) as we can recover V2 from V1.
This gives us [200][10000] states.
This candidate WORKS within the Memory Limit! :)
10
Suppose a previously achievable state is (i, V1, V2). (I write
three parameters for clear explanation, though we do not
store 3-parameter states physically)
We are considering the i-th object of size Vi.
We have two choices with respect to this previously
achievable state (i, V1, V2):
1) Put it into the first container (when V1+Vi <= L)
(i, V1, V2) -> (i+1, V1+Vi, V2)
2) Put it into the second container (when V2+Vi <= L)
(i, V1, V2) -> (i+1, V1, V2+Vi)
11
First, for each object we have to enumerate the previously
achievable states, which is O(L).
For each object, we need to consider two decisions, which is
constant time.
We have to do the above for each of the objects. We multiply
the time by the number of objects. Therefore, we have finally
an O(NL) time DP solution, where N<=200, L<=10000.
Now we know, this solution really WORKS, as both time and
space requirements are satisfied.
12
Don’t forget that the problem asks us to print the actual
solution in addition to the maximum number of objects we
can pack.
This is a DP solution memo requirement.
We can do it without increasing our space requirement.
• When we reach a new state from a previous one, we can store a
backward pointer of the new state to the previous state.
• In this way later we can trace back from the final state all the way to
the initial state and figure out every decision, via the backward
pointers.
• Each state of DP has exactly one backward pointer, so the backward
pointers together take the same space as the dp states.
13
L = 5000, {2500, 3000, 1000, 1000, 1500, 700, 800}
5000 (2500)
4500 (3000)
2500 (0)
0
0 (2500)
i=0
i=1
2500
4000 (2500)
4000 (3500)
3500 (3000)
3500 (4000)
3000 (2500)
3000 (3500)
3000 (4500)
2500 (3000)
2500 (4000)
2500 (5000)
i=2
3000
i=3
1000
i=4
1000
14
L = 5000, {2500, 3000, 1000, 1000, 1500, 700, 800}
5000 (2500)
5000 (4000)
5000 (4700)
4500 (3000)
4500 (4500)
4700 (5000)
4000 (3500)
4000 (5000)
3500 (4000)
3000 (4500)
2500 (5000)
i=4
1000
i=5
1500
i=6
700
i=7
800
15
L = 5000, {2500, 3000, 1000, 1000, 1500, 700, 800}
Container 2
Container 1
5000 (2500)
5000 (4000)
5000 (4700)
4500 (3000)
4500 (4500)
4700 (5000)
4000 (3500)
4000 (5000)
3500 (4000)
3000 (4500)
2500 (5000)
i=4
1000
i=5
1500
i=6
700
i=7
800
16
L = 5000, {2500, 3000, 1000, 1000, 1500, 700, 800}
Container 2
5000 (2500)
Container 1
4500 (3000)
4000 (2500)
4000 (3500)
3500 (3000)
3500 (4000)
3000 (2500)
3000 (3500)
3000 (4500)
2500 (3000)
2500 (4000)
2500 (5000)
i=2
3000
i=3
1000
i=4
1000
Container 1
Container 1
2500 (0)
0
0 (2500)
i=0
i=1
2500
17
Pay attention to the input format.
• L is given in meters, but lengths of cars are in centimeters. So we shall
do an easy conversion.
• As the number of cars is unknown beforehand, the input car length
sequence of each case is terminated by 0.
• If one car cannot be loaded, all the following cars cannot be loaded
either. But we still need to read in the other car lengths.
• Allocate arrays with redundant entries, or be careful with off-by-one
errors. Note that a minimum of L+1 entries is required for each DP
table row.
18
Test your code with predesigned test cases.
For example:
L = 1, {10000}
L = 1, {100, 100}
…
Cannot load any car! output 0
One car at each side
Usually if you have a clear thinking of your DP logic (time, space, state
transition, etc.), you have a high chance of getting 1A (got Accepted using
1 submission).
Finally,
Submit and have fun! :D
19
I’ve written one C++ solution and one Java solution for this
problem, which will be given out with this slide.
(In fact for this problem, you can use space saving trick if you like for the
DP table, but not for the backward pointers.)