5

I have an interview lined up for the next week and am practicing problems for the same. I came across this question, could you please help me with how to solve it?

There are A gift vending machines and you have B pouches to collect the gifts in. Each gift vending machine has unlimited gifts.

Each machine starts dropping the gift at time Si second and keeps dropping a gift every Ii seconds. You can arrange the pouches in any way you like but once they are set below one machine, you cannot move it to another machine. You need to collect a minimum of C gifts in minimum time.

Input

The first line contains the integers A, B, C.

The second line contains integers, Si. ( separated with a space), I goes from 1 to A.

The third line contains integers Ii ( separated with a space), I goes from 1 to A.

Output

An integer which gives the minimum time calculated.

The method I thought was pretty inefficient. I thought that we can have all subsets with their cardinal number equal to B and then choose the one which gives the minimum time.

This approach seems to be brute force, so I'm looking for another alternative approach.

Can any of you please help me with it?

stefan.schroedl
  • 866
  • 9
  • 19
Matt Conor
  • 51
  • 3
  • 1
    You may wish to look into dynamic programming, for example the weighted interval scheduling problem. – Hungry Oct 09 '14 at 07:24
  • I did think of doing it using DP but don't know what to do. The max I can think of using DP is, after every second, finding a subset with cardinality=B whose sum will be >=C Could you please elaborate on how you'd solve this using DP? – Matt Conor Oct 09 '14 at 07:48
  • I haven't done DP in years and unfortunately I dont really have the time right this second to brush up on it! I think that you are heading in the right direction though, and if it's practice for an interview then it seems a little pointless asking others for a solution. – Hungry Oct 09 '14 at 08:19

4 Answers4

1

First of all write a function

int max_num_gifts(A,B,t,S[],l[])

which calculates the maximum number of gifts that you can collect with B bags in t time. This can be done in O(A) time: given S[] and l[] the number of gifts the machine A[i] drops is (t-S[i])/l[i]+1. Then get the top B machines which drop the most gifts. See How to find the kth largest element in an unsorted array of length n in O(n)? on how to do the selection in O(n) time.

Then you can just loop through the times t=1,2,... until max_num_gifts >= C. Or better still, you can use a binary search to find such a t: Start with t=1, then check t=2, t=4, t=8 etc until you get too big a number, then binary search for the required t.

The overall time complexity should be O(A* lg(T_min)).

Community
  • 1
  • 1
Jason L
  • 510
  • 5
  • 16
0

One possible approach can be as follows:

Simulate each vending machine simultaneously by stepping through the events. For each machine calculate the time after which it will drop the gift and step through that time.

Also, maintain a priority queue of all the machines, ordered by the number of gifts dropped till present.

Then when the sum of gifts top B machines equals C, that is the minimum time T_min.

Order of complexity: O( T_min * A * logA )
Abhishek Bansal
  • 12,589
  • 4
  • 31
  • 46
0

First you have to sort the starting times S_i of the vending machines in A in ascending order. With this prerequisite, I think, the problem get's clearer if you write it as an equation:

Problem as euqation

Where \Theta is the Heaviside step function and the special braces denote the floor function.

A small python script to find T when gamma exceeds C could look like

import numpy as np

A=10
B=3
C=10
S=np.sort(np.random.randint(100, size=A))
gamma=0
t=1

while gamma<C:
    for i in range(1, B):
        gamma=(1 if t-S[i] > 0 else 0) * np.floor(t/2)
        t = t+1

print "minimal t: %d" % t

I did not read the input values A, B and C from a file and generated the starting times S randomly between 0 and 100. You also would need to check if B<A.

Joachim Rosskopf
  • 1,259
  • 2
  • 13
  • 24
0

For simplicity, let's make the following assumptions (we can relax these later and work out the edge cases):

  1. Each machine produces a 'fractional gift' of 1/l[i] at every time step.
  2. All S[i] are different, i.e., no two machines start dispensing at the same time.

Let's consider the two border cases first:

  • If we have more pouches than machines, we don't have to choose between them; at every time step S[i], we add a pouch to machine i, and keep collecting all gifts until we have a total of C.
  • If |B| = 1 (only one pouch), we add the pouch at the minimum S[i]. We keep stepping through time until either we reach C, or until another machine would have led to a higher total at that time, then switch the pouch due to 20-20 hindsight. Note that mathematically, the switch point is the intersection of the two lines that cross the x-axis at S[i], and have slope 1/l[i]. We will come back to that later.

Above considerations lead to the following general (naive) algorithm, in pseudocode.

def min_time_to_reach_c(S,l):
    def function yield(m, t) = max(0, t-S[m])/l[m]

    Active = {} # The currently best set of machines that we attach our pouches to
    Inactive = {1,..,A} # The other machines
    t = 0 # time step
    sum = 0 # total gifts collected so far
    gift_rate = 0 # gifts we receive at every time step

    while sum < C:
       if |Active| < B and t == S[m’] for some m’ in InActive:
          # we have not used up all our pouches yet
          Active += {m’}
          InActive -= {m’}
          gift_rate += l[m’]
       else if yield(m’,t) > yield(m,t) for some m’ in InActive, m in Active: (*)
         # for all successive time steps, m’ will be preferable to m
         Active   += {m’} - {m}
         InActive -= {m’}
         sum += yield(m’,t) - yield(m,t)
         gift_rate += l[m’] - l[m]
       sum += gift_rate
       t += 1
   return t

The complexity of this naive algorithm is at most O( t_min * A), if in each step (*) we determine the 'crossing' by finding the maximum (minimum) yields of the (In)Active sets by one linear pass each.

If A < t_min, we can actually do better by thinking about the geometry - it can be viewed as a sweep line algorithm. In fact, it is not necessary to visit each time point, we only need to look at the 'interesting' points where a machine starts or lines cross. In a preprocessing step, we calculate all time steps with intersections (if a line does not cross another one and is dominated, it can be dropped right away). At each such point, we can update our active set and the yield rate as above. We also calculate the distance to reach C at the current rate; if this occurs before the next S[i] or crossing, we are done.

The complexity of the sweep line algorithm is O(A^2) for the preprocessing step, plus another O(A^2) for the at most 2A update steps (or better, O(A log A) if we use a priority queue to keep track of the relative order of lines). Note that this is independent of t_min.

I think an interesting follow-up question would be: What would be the best strategy if you didn't know the S[i] and l[i] in advance?

stefan.schroedl
  • 866
  • 9
  • 19