5

I encountered this problem on an online screening test of a company a few days ago. The problem statement reads like this:

There are n people standing in line to buy show tickets.Due to high demand, the venue sells tickets according to the following rules:

  • The person at the head of the line can buy exactly one ticket and must then exit the line.
  • if a person needs to purchase additional tickets,they must re-enter the end of the line and wait to be sold their next ticket(assume exit and re-entry takes zero seconds).
  • Each ticket sale takes exactly one second.

We express initial line of n people as an array, tickets = [tickets0, tickets1 ... ticketsN-1], where ticketsi denotes the number of tickets person i wishes to buy. If Jesse is standing at a position p in this line, find out how much time it would take for him to buy all tickets. Complete the waiting time function in the editor below. It has two parameters:

  1. An array, tickets, of n positive integers describing initial sequence of people standing in line. Each ticketsi describes number of tickets that a person waiting at initial place.
  2. An integer p, denoting Jesse's position in tickets.

    Sample Input 5 2 6 3 4 5 2 Sample Output 12 Sample Input 4 5 5 2 3 3 Sample Output 11

During the time of the test, i came up with this simple approach which passed most of the test cases but was getting timed out on a few test cases:

long waitingTime(vector<int> tickets, int p) {
  // bool flag indicates whether it's Jesse or not
  queue<pair<int, bool> > aQueue;

  for(int i = 0; i < tickets.size(); i++) {
    aQueue.push(make_pair(tickets[i], i == p));
  }

  long long nTime = 1;
  while(!aQueue.empty()) {
    pair<int, bool> aItem = aQueue.front();
    aQueue.pop();
    nTime++;
    if(aItem.first == 1 && aItem.second == true)
      break;
    else if(aItem.first > 1) {
      aQueue.push(make_pair(aItem.first-1, aItem.second));
    }
  }
  return nTime-1;
}

But i'm not able to find a different approach to solve this problem. I think there is some other way without having to simulate the whole queue flow. I would really appreciate if someone could provide me with the right approach of solving this. Thanks in advance!

Rohan Kumar
  • 4,060
  • 6
  • 23
  • 33
  • 1
    Give us a minimal test case please as required ([MCVE]). – πάντα ῥεῖ May 13 '17 at 06:36
  • See the sample input output test cases i provided. Moreover i'm not asking you all to debug/ solve my code. I'm just asking for some different way to solve the problem. I don't have the problem link so i can't share it. – Rohan Kumar May 13 '17 at 06:36
  • I've done another approach from which I believe it works. However I get a different result on your 1st test set. The reason, I believe, is that the result is dependent of the index of "Jesse". Could you, please, denote the `p`s for your input samples? – Scheff's Cat May 13 '17 at 07:02
  • Yeah, it depends on index of Jesse. Input starts with N, number of people in queue initially, than N tickets[i] values and last integer p, index of jesse. – Rohan Kumar May 13 '17 at 07:10
  • Sure! I don't have any problem. You can simply share psuedocode as well. – Rohan Kumar May 13 '17 at 07:14
  • After reading your comment again, I realized my mistake in the sample inputs and fixed them. Now, I get your expected output. – Scheff's Cat May 13 '17 at 12:14

13 Answers13

19

All the test cases on HackerRank are passed. The simplest solution to this is -

def waitingTime(tickets, p):
    total_steps = tickets[p]
    first_half = tickets[:p]
    second_half = tickets[p+1:]

    for num in first_half:
        if num < tickets[p]:
            total_steps += num
        else:
            total_steps += tickets[p]

    for num in second_half:
        if num < tickets[p]:
            total_steps += num
        else:
            total_steps += tickets[p] - 1

    return total_steps

Explanation -

  1. Divide list into two halves. People standing ahead of Jesse and persons standing behind Jesse.
  2. Check with each person in both the halves - how many tickets that person wants to buy.
  3. Lets consider first half
  4. If the person wants to buy less number of tickets than that of Jesse - the person will visit the ticket window till he buy the tickets before Jesse. So add his number of tickets to the total unit time
  5. If the person wants to buy more or the equal tickets than Jesse then he will visit ticket window before Jesse exactly the same number of times that Jesse visits the ticket window. So add Jesse's tickets count to the total unit time - which is equal to the person's ticket count which he buys before Jesse
  6. Now consider second half -
  7. If the person standing behind Jesse wants to buy more or equal tickets than Jesse, he will visit ticket window one less time than Jesse. So add (Jesse's ticket count - 1) to the total unit time
  8. If the person standing behind Jesse wants to buy less tickets than Jesse, then the person will visit ticket window until he buys all his tickets. So add persons total ticket count to the total unit time.
  9. Finally, add Jesse's ticket count to the total unit time too, because Jesse will also visit the ticket window until he buys all the tickets that he wants

e.g. Five persons are standing in a queue. Their ticket count is given in the list below. Jesse is standing at 3rd position (list index = 2)

[2,6,3,4,5]

first half = [2,6] second half = [4,5]

now consider first half -

  1. Person#1 wants to buy 2 tickets. Jesse's count (3) is greater than 2. So this person will definitely visit ticket window twice before Jesse. Hence total_unit_time = 2

  2. Person#2 wants to buy 6 tickets. Jesse's count (3) is less than 6. So this person will visit ticket window 3 times before Jesse. So total_unit_time = 2+3

now consider second half - 1. Person#1 wants to buy 4 tickets. Jesse's count (3) is less than 4. Now, Jesse will buy his first ticket then the person will get a chance to buy his first ticket. But then Jesse will have to wait for 2 more turns to buy remaining 2 tickets after this person. So total_unit_time = 2+3+(3-1)

  1. Person#2 wants to buy 5 tickets. Again Jesse will buy his first ticket and will wait for his remaining two turns until this guy buys two tickets. So total_unit_time = 2+3+2+(3-1)

enter image description here

Abhishek Kulkarni
  • 3,169
  • 4
  • 28
  • 38
10

Looking at the problem twice, I got the idea that an analytical solution should be possible.

My idea is:

  1. People i before Jesse will stay in front of him min{ ticketsi, ticketsJesse } times.
  2. Jesse himself will consume ticketsJesse times.
  3. People i after Jesse will stay in front of Jesse min{ ticketsi, ticketsJesse - 1 } times.

Thus, it should be possible to simply sum up the numbers in one loop. This would mean O(n) instead of O(n2).

As I realized, that the result will also depend on the position of Jesse. However, my results looked somehow different than the sample output. Thus, I implemented a naïve solution (similar to the OP) also. The source waiting-queue.cc:

#include <algorithm>
#include <iostream>
#include <queue>
#include <vector>

// naive approach

int waitingTimeSim(const std::vector<int> &tickets, int p)
{
  // setup sim. model
  std::queue<std::pair<int, bool> > people;
  for (int i = 0, n = (int)tickets.size(); i < n; ++i) {
    people.push(std::make_pair(tickets[i], i == p));
  }
  // simulation
  int tP = 0;
  for (;;) {
    std::pair<int, bool> person = people.front();
    people.pop();
    --person.first; ++tP; // buy ticket
    if (!person.first) { // if person is done
      if (person.second) return tP; // It's Jesse -> terminate
    } else people.push(person);
  }
}

// analytical approach

int waitingTime(const std::vector<int> &tickets, int p)
{
  int tP = 0, ticketsP = tickets[p];
  for (int i = 0, n = (int)tickets.size(); i < n; ++i) {
    tP += std::min(tickets[i], ticketsP - (i > p));
    // i > p -> people after jesse -> decr. by 1
  }
  return tP;
}

int main()
{
  { std::vector<int> tickets{ 2, 6, 3, 4, 5 };
    for (int p = 0, n = tickets.size(); p < n; ++p) {
      std::cout << "tickets{ 2, 6, 3, 4, 5 }, p = " << p << std::endl;
      int tS = waitingTimeSim(tickets, p);
      std::cout << "simulated t: " << tS << std::endl;
      int t = waitingTime(tickets, p);
      std::cout << "computed t:  " << t << std::endl;
    }
  }
  { std::vector<int> tickets{ 5, 5, 2, 3 };
    for (int p = 0, n = tickets.size(); p < n; ++p) {
      std::cout << "tickets{ 5, 5, 2, 3 }, p = " << p << std::endl;
      int tS = waitingTimeSim(tickets, p);
      std::cout << "simulated t: " << tS << std::endl;
      int t = waitingTime(tickets, p);
      std::cout << "computed t:  " << t << std::endl;
    }
  }
  return 0;
}

I uploaded the source to ideone.com.

My test session (g++, cygwin, Windows 10):

$ g++ -std=c++11 -o waiting-queue waiting-queue.cc 

$ ./waiting-queue.exe 
tickets{ 2, 6, 3, 4, 5 }, p = 0
simulated t: 6
computed t:  6
tickets{ 2, 6, 3, 4, 5 }, p = 1
simulated t: 20
computed t:  20
tickets{ 2, 6, 3, 4, 5 }, p = 2
simulated t: 12
computed t:  12
tickets{ 2, 6, 3, 4, 5 }, p = 3
simulated t: 16
computed t:  16
tickets{ 2, 6, 3, 4, 5 }, p = 4
simulated t: 19
computed t:  19
tickets{ 5, 5, 2, 3 }, p = 0
simulated t: 14
computed t:  14
tickets{ 5, 5, 2, 3 }, p = 1
simulated t: 15
computed t:  15
tickets{ 5, 5, 2, 3 }, p = 2
simulated t: 7
computed t:  7
tickets{ 5, 5, 2, 3 }, p = 3
simulated t: 11
computed t:  11

$

Note:

IMHO, the name waitingTime() is a little bit mis-leading because the assignment says clearly that you have to

find out how much time it would take for him to buy all tickets.

Thus, my implementation counts the waiting time + the time Jesse needs to buy his last ticket.

Update:

An often used German phrase says: Who can read is clearly in the advantage. (It doesn't sound so nice and handy in English like in German: „Wer lesen kann, ist klar im Vorteil.“) However, after reading again the comment-answer of Rohan Kumar to my comment-question, I adjusted my sample input to the OP and, suddenly, got the expected output. However, the algorithms didn't change.

Community
  • 1
  • 1
Scheff's Cat
  • 16,517
  • 5
  • 25
  • 45
6

Here is my idea to solve this problem, first take a look at the line. We divide people in 2 groups:
1/ Those stands before pth person.
2/ Those stands behind pth perosn.
Let's call times - the number of move that pth person has to take to buy sufficent tickets.

Now considering the first group [tickets0, tickets1, ..., ticketsP-1], if there is a person i who needs to buy the number of tickets smaller than the pth person, then just simply add <ticket i> to times ( the duration that pth person has to wait for the person i until he gets out of the line ). Otherwise, in case the amount of buying tickets of person i is bigger than person pth, add <ticket p> to times.

Secondly, the same idea to those who stand behind the pth person [ticketsP+1, ticketsP+2, ..., ticketsN]. Considering person t (t > p), we add <ticket t> to times if ticketT < ticketP. Unless person t buys tickets less then person p, skip the last round so that just add <ticket p - 1 > to times

While iterating the lines, dont forget to add ticket p to times whenever meet the person p.

public static long times( int[] tickets, int p) {
    long times = 0;
    int[] temp = Arrays.copyOf(tickets, tickets.length); //creating this array to check whether the *person i* buy tickets less than *person p*
    for(int i = 0; i < tickets.length; i++ ) {
       temp[i] = tickets[i] - tickets[p];
    }
    for(int i = 0; i < tickets.length; i++ ) {
       if(temp[i] < 0) times += tickets[i];
       else {
          if(i <= p) times += tickets[p];
          else times += tickets[p] - 1;
       }
    }
    return times;
} 

Explain:
Sample Input 4 5 5 2 3 3 Sample Output 14
p = 4, 14 = 3 + 3 + 2 + 3 + 3

Long Tran
  • 63
  • 3
  • 5
1

Here's the solution I came up with (in Java, but anyone with c++ background can read this code)

import java.util.Arrays;
import java.util.List;

public class TickerPurcahseTime {
  public static int calculateTimeOptimized(List<Integer> line, int pos) {
    // Minimum time I have to wait is the number of tickets I want to buy.
    int waitingTime = line.get(pos);
    for (int i = 0; i < line.size(); i++) {
      if (i == pos) continue;
      // I will see people -> minimum(how many tickets I need, how many tickets they need).
      waitingTime += (Math.min(line.get(pos), line.get(i)));
      // Unless they were behind me, I got my ticket before them so I need to see them 1 time less.
      if (i > pos) {
        waitingTime--;
      }
    }

    return waitingTime;
  }

  public static void main(String [] args) {
    System.out.println(calculateTimeOptimized(Arrays.asList(5, 5, 2, 3), 3));
  }
}
Nayeem Zen
  • 2,136
  • 1
  • 15
  • 16
1

Solution in Java :

static long waitingTime(int[] tickets, int p) {
        long noOfIterations = 0;
        int ticketBeingProcessed = 0;
        int numberOfParticipantsInLine = tickets.length;
        if(numberOfParticipantsInLine > p)
        {
            while(tickets[p] != 0)
            {
                // The person has already got his ticket and exited the line, just go to the next person, dont increase number of iterations because it took no time
                if(tickets[ticketBeingProcessed] != 0)
                {
                    // ticket being processed got one ticket
                    tickets[ticketBeingProcessed] = tickets[ticketBeingProcessed] -1;
                    // if we have reached the end of the line
                    if(ticketBeingProcessed == numberOfParticipantsInLine -1)
                        ticketBeingProcessed = 0;
                    else
                        ticketBeingProcessed ++;
                    noOfIterations ++;
                }
                else {
                    if (ticketBeingProcessed == numberOfParticipantsInLine - 1)
                        ticketBeingProcessed = 0;
                    else
                        ticketBeingProcessed++;
                }
                Log.d("asd",printArray(tickets));
            }
        }
        return noOfIterations;
    }
nb07
  • 181
  • 2
  • 11
legalimpurity
  • 190
  • 3
  • 12
1

so here is my O(n) complex and easily understandable solution

# people stading before jess will be waiting for t(p) times 
# (or)
# t(i) times (if t(i) - t(p) < 0 this means they will buy less tickets than jess, 
# so they will stand only t(i) times)
#
# people standing beside jess will be waiting for t(p)-1 times
# (or)
# t(i) time (if t(i) - t(p) < 0 )
#
# sum of the above will result in the wating time for jess

def wt(tickets, p):
    sum = 0;
    for i in range(len(tickets)):
        if i<=p :
            if tickets[i]-tickets[p] >= 0 :
                sum += tickets[p];
            else :
                sum += tickets[i];
        else:
            if tickets[i]-(tickets[p]-1) >= 0 :
                sum += tickets[p]-1;
            else:
                sum += tickets[i];
    print(sum);

test cases:

wt([5,5,2,3], 3)

wt([5,5,2,3], 0)

wt([5,5,2,3], 1)

wt([5,5,2,3], 2)

wt([1,1,1,1], 0)

wt([2,6,3,4,5], 2)

wt([2,6,3,4,5], 0)

wt([2,6,3,4,5], 1)

wt([2,6,3,4,5], 3)

wt([2,6,3,4,5], 4)

outputs:

11

14

15

7

1

12

6

20

16

19
hari
  • 69
  • 4
1

For python:

def function(tickets):
    count = 0
    delta = 0
    for i in range(len(tickets)):
        if tickets[i] < tickets[p]:
            delta+=tickets[p]-tickets[i]
            if i > p:
               delta - = 1

    count = len(tickets) * (tickets[p] - 1) + (p+1) - delta

    return count
0

Here's the simple C++ Solution:

#include<bits/stdc++.h>
using namespace std;
int waiting_time(int ticket[],int p,int size)
{
  int count=0;
  while(ticket[p]!=0)
  for(int i=0;i<size;i++)
  {
    if(ticket[p]!=0)
    {
      if(ticket[i]>0)
      count++;
      ticket[i]--;
    }
  }
  return count;
}
int main()
{
  int ticket[]={5, 5, 2, 3};
  int p=3;
  int size=sizeof(ticket)/sizeof(ticket[0]);
  cout<<waiting_time(ticket,p,size);
  return 0;
}
0

Here is simple solution using ruby

    def waiting_line(tickets, p)
      user_tickets = tickets[p]
      time = 0
      while user_tickets > 0
        for i in (0...tickets.length)
          break if tickets[p] == 0
          next if (tickets[i] == 0) 
          time +=1 
          tickets[i] -= 1
        end
        user_tickets -=1
      end
      time
    end
0

How about this one in python

def calculateTickets(l,p):
    j=int(0)#contador de
    while(l[p]!= 0 ):
        i=0
        for i in range (len(l)):
            if(l[p]==0):
                break
            if(l[i]>0):
                l[i]-=1
                j+=1
            i+=1
    print(str(j)+"\n")
Jorge
  • 1
0

Solution: C#

    /// <summary>
    /// Buying show tickets
    /// </summary>
    public static void BuyingShowTickets()
    {
        int[] tickets = { 2, 6, 3, 4, 5 };
        int alexPos = 2;

        Queue queue = new Queue();

        foreach (int tik in tickets)
            queue.Enqueue(tik);

        int alexValue = tickets[alexPos];
        int queuePopValue = 0;
        int queuePopNewValue = 0;
        int noOfTransactions = 0;
        
        //Loop until Alex has bought all the tickets
        while (alexValue != 0)
        {
            //Total no of tickets to buy
            queuePopValue = (int)queue.Peek();
            //Buy one ticket
            queuePopNewValue = queuePopValue - 1;
            //Move out from the queue
            queue.Dequeue();
            //Increase the number of iteration
            noOfTransactions++;
            //If you still have more tickets to buy go to the end of the line
            if (queuePopNewValue != 0)
            {
                queue.Enqueue(queuePopNewValue);
            }
            //Track where Alex is now. If Alex is buying the ticket and 
            //he has more tickets to buy then go to the end of the line
            //else just more forward
            if (alexPos > 0)
            {
                alexPos--;                  
            }
            else
            {
                alexPos = queue.Count - 1;
                alexValue--;
            }
        }

        Console.WriteLine("Total time taken: {0}",noOfTransactions);

    }
0

Simple code in Python

# position is in zero based indexing
def calculate_time(pos, line):
    # get the number of tickets at given position
    value = line[pos] 
    # split the people before and after the given position
    people_before = line[:pos+1] 
    people_after = line[pos+1:]
    time_taken = 0
    while value > 0:
      # reduce 1 from each of the person
      people_before = [x-1 for x in people_before] 
      time_taken += len(people_before) 
      # remove the persons with zero tickets
      people_before = list(filter(lambda x: x>0, people_before))
      value -= 1
      if value != 0: # if the number of ticket at given position becomes zero, stop
        people_after = [x-1 for x in people_after]
        time_taken += len(people_after)
        people_after = list(filter(lambda x: x>0, people_after))
    return time_taken

jesse_pos = 4
line = [2, 6, 3, 4, 5 ]
print(calculate_time(jesse_pos, line))

OUTPUT: 19

Time complexity : O(n)

Rebekkal
  • 1
  • 1
  • This is not strictly an O(n) solution as it is dependant on the number of tickets everyone wants to buy consider the starting queue of [10^100, 10^99, 10^100, 10^200] – geoff3jones Nov 03 '20 at 14:01
-1

This is a different approach using ruby, but can easy be translated to C or Java.

def incr_i n, i
  i += 1
  i = 0 if i >= n
  i
end

def waitingTime(tickets, p)
  time = 0
  i = 0
  n = tickets.size
  # Note: a timeout can be added for avoiding infinite loops
  while true
    next i = incr_i(n, i) if tickets[i] <= 0
    #puts "#{tickets.to_s}, i: #{i}, time: #{time}"
    tickets[i] -= 1
    time += 1
    break if tickets[p] <= 0
    i = incr_i(n, i)
  end

  time
end