Hackerrank购买演出门票优化

时间:2017-05-13 06:32:18

标签: c++ algorithm

我几天前在一家公司的在线筛选测试中遇到了这个问题。问题陈述如下:

  

有n个人排队购买演出门票。由于很高   要求,场地按照以下规则出售门票:

     
      
  • 该行的负责人可以购买一张票,然后必须退出该行。
  •   
  • 如果一个人需要购买额外的门票,他们必须重新进入线路末端并等待出售他们的下一张门票(假设退出   并且重新进入需要零秒。)
  •   
  • 每张门票只需一秒钟。
  •   
     

我们将n个人的初始行表示为数组,ticket = [tickets0,   tickets1 ... ticketsN-1],其中ticketi表示门票数量   我想买的人。如果杰西站在这个位置p   ,找出他需要花多少时间购买所有门票。   在下面的编辑器中完成等待时间功能。它有两个   参数:

     
      
  1. n个正整数的数组,票据,描述排队的人的初始顺序。每个ticketi描述的数量   一个人在最初的地方等候的门票。
  2.   
  3. 整数p,表示杰西在故障单中的位置。

         

    样品输入5 2 6 3 4 5 2样品输出12   样品输入4 5 5 2 3 3样品输出11

  4.   

在测试期间,我想出了这个简单的方法,它通过了大多数测试用例,但是在一些测试用例上超时了:

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;
}

但我无法找到解决此问题的不同方法。我认为还有其他方法,无需模拟整个队列流。如果有人能为我提供正确的解决方法,我将非常感激。提前谢谢!

12 个答案:

答案 0 :(得分:8)

两次看问题,我认为应该有可能采用分析解决方案。

我的想法是:

  1. 之前Jesse将留在他面前 min {ticket i ,门票 Jesse }
  2. 杰西自己将消耗门票 Jesse 次。
  3. 人们杰西将留在杰西前面 min {ticket i ,门票 Jesse - 1} 次。
  4. 因此,应该可以简单地在一个循环中总结数字。 这将意味着O(n)而不是O(n 2 )。

    我意识到,结果也取决于杰西的位置。 但是,我的结果看起来与样本输出有所不同。 因此,我也实施了一个天真的解决方案(类似于OP)。 来源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;
    }
    

    我将来源上传到 ideone.com

    我的测试会话(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
    
    $
    

    注意:

    恕我直言,名称waitingTime()有点误导,因为作业明确说明你必须

      

    了解他购买所有门票需要多长时间。

    因此,我的实施计算了等待时间 + 杰西需要购买他的最后一张票的时间。

    更新

    一个经常使用的德语短语说:谁能读懂显然是有利的。 (听起来听起来不像德语那样好用和方便:“Wer lesen kann,ist klar im Vorteil。”)然而,在再次阅读Rohan Kumar的评论答案后,我的评论问题,将我的样本输入调整到OP,突然得到了预期的输出。但是,算法没有改变。

答案 1 :(得分:6)

这是我解决这个问题的想法,先来看看这一行。我们将人分为两组:
1 /那些站在 pth 之前 2 /那些站在 pth perosn后面 我们打电话 - 购买足够票的 pth 人必须采取的行动次数。

现在考虑第一组[ tickets0,tickets1,...,ticketsP-1 ],如果有一个人 i 需要购买小于 pth 人的票数,然后只需添加&lt; ticket i &gt; pth 人必须等待之前的持续时间,直到他离开线路为止)。 否则,如果 i 的人购票金额大于人 pth ,请添加&lt; ticket p &GT;

其次,对于那些站在 pth 人[ ticketsP + 1,ticketsP + 2,...,ticketsN ]后面的人也有同样的想法。考虑到人 t t&gt; p ),我们添加&lt; ticket t &gt;如果 ticketT &lt; ticketP 即可。除非 person t 购买的门票少于 person p ,否则跳过最后一轮,只需添加&lt; ticket p - 1&gt;至

在迭代这些行时,不要忘记每当遇到 person p 时,将 ticket 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;
} 

说明:
样本输入 4 5 5 2 3 3 样本输出 14
p = 4,14 = 3 + 3 + 2 + 3 + 3

答案 2 :(得分:6)

通过HackerRank上的所有测试用例。最简单的解决方案是 -

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

说明 -

  1. 将列表分成两半。站在Jesse前面的人和站在Jesse身后的人。
  2. 检查两个人中的每个人 - 该人想要购买多少张门票。
  3. 让我们考虑上半年
  4. 如果此人想购买的门票数量少于Jesse的门票数量,那么此人将访问售票窗口,直到他在Jesse 之前购买门票。因此,将他的门票数量添加到总单位时间
  5. 如果此人想购买比Jesse更多或相同的门票,那么他将在Jesse 之前访问售票窗口与Jesse访问售票窗口的次数完全相同。因此,将Jesse的门票数量添加到总单位时间 - 这相当于他在Jesse之前购买的人数。
  6. 现在考虑下半场 -
  7. 如果站在Jesse身后的人想要购买比Jesse更多或更多的门票,他将比Jesse更少的时间访问售票窗口。所以将(Jesse的票数 - 1)添加到总单位时间
  8. 如果站在Jesse身后的人想要购买比Jesse少的门票,那么该人将访问售票窗口,直到他买完所有门票。因此,将总票数计入总单位时间。
  9. 最后,将Jesse的门票数量也计入总单位时间,因为Jesse也会访问售票窗口,直到他购买了他想要的所有门票
  10. e.g。五个人站在队列中。他们的票数在下面的列表中给出。 Jesse站在第3位(列表索引= 2)

    [2,6,3,4,5]

    上半场= [2,6] 下半场= [4,5]

    现在考虑上半年 -

    1. #1号人想买2张票。 Jesse的数量(3)大于2.所以这个人肯定会在Jesse之前两次访问售票窗口。因此,total_unit_time = 2

    2. 第2号人想购买6张门票。 Jesse的数量(3)小于6.所以这个人将在Jesse之前3次访问售票窗口。所以total_unit_time = 2+ 3

    3. 现在考虑下半场 - 1.#1人想买4张票。 Jesse的数量(3)小于4.现在,Jesse将购买他的第一张票然后该人将有机会购买他的第一张票。但是杰西将不得不等待2个回合才能购买剩余的2张门票。所以total_unit_time = 2 + 3 + (3-1)

      1. #2号人想买5张票。 Jesse将再次购买他的第一张票,并等待他剩下的两个回合,直到这个人买了两张票。所以total_unit_time = 2 + 3 + 2 + (3-1)
      2. enter image description here

答案 3 :(得分:1)

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;
    }

答案 4 :(得分:1)

对于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

答案 5 :(得分:0)

这是我提出的解决方案(在Java中,但是有c ++背景的人都可以阅读此代码)

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));
  }
}

答案 6 :(得分:0)

这是简单的C ++解决方案:

#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;
}

答案 7 :(得分:0)

这是使用红宝石的简单解决方案

    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

答案 8 :(得分:0)

这个在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")

答案 9 :(得分:0)

解决方案: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);

    }

答案 10 :(得分:0)

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))

输出: 19

时间复杂度:O(n)

答案 11 :(得分:-1)

这是一种使用ruby的不同方法,但可以很容易地转换为C或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