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. Task: Maximize the number of cars you can load, and print a way of loading maximum number of cars (any will do). 2 L = 5000, Cars = {2500, 3000, 1000, 1000, 1500, 700, 800} Answer: 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 more straightforward model instead. 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 forget about complete search. 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 Good thing about DP: 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.) I’ve added exhaustive comments for each statement, and expand each variable to its full name (instead of contest-style short name) so as to minimize the chance you can confused. Hope they help! 20 21