简体   繁体   中英

Time complexity on simulation exercise

I am having a problem with algorithm complexity, I have attempted the solution below thinking it was easy when i forgot to take into account the time constraint. I have added my code below which I'm not even sure the complexity of. I am curious to see what the O(N) or O(K) solution would be. If someone could help out with this it would be appreciated greatly.

Time Limit: 1 second

There are K seats available, each represented by a physical seat at a point around a circle. The K+1 people are also initially standing at points around the circle. Points on the circle are labelled clockwise from 1 to N, such that point 1 immediately follows point N. No two people will be initially standing at the same point, and no two chairs will be at the same point.

Each second, all the people who are still standing do the following (simultaneously):

  • If the person is standing at the same point as an empty chair, the person will sit down in it.

  • Otherwise, the person will step one place clockwise around the circle to the next point. If the person was previously at point i (with i < N), the person will now be at point i+1. If the person was previously at point N, the person will now be at point 1.

Since there are K+1 people, eventually all K seats will be taken and the one person remaining without a seat. The person sitting in the first seat in the circle will have the best place. (The 'first' seat in the circle is defined as the first seat clockwise from point 1.)

Your task is to determine who will be in the first seat and who will be the person left standing up.

Input N and K.
K space-separated integers representing the points at which there are chairs, in increasing order. Hence, the first chair in this list will be the first seat
K+1 space-separated integers representing the points of people, in increasing order. The people are numbered from 1 to K+1 by their position in this list.

1 ≤ N ≤ 1000000
1 ≤ K ≤ 100000

Output
Person in the first seat
Person left standing

Sample   

Input   
10 3   
2 5 8   
3 4 6 8   

Output   
3   
1   

初始配置

In the first second, the fourth person (at point 8) will immediately sit down on the chair beneath them. The other three people will move one place around the circle. In the next second, the second person (now in position 5) will sit down on the second seat. The first and third people will continue walking around the circle until the third person reaches position 2 and sits down, leaving the first person with no chair to sit in.

while(standing != 1)
{
    for (int i = 0; i < K + 1; i++)
    {
        if (sitting[i] == 0)
        {
            people[i]++;

            if (isSitting(people[i], chairs,i)) //this function checks if the current person is at a chair
            {
                sitting[i] = 1;
                standing--;
            }

            if (people[i] > N)
            {
                people[i] = 1;
            }
        }
    }

    }
   standingPerson = indexOf(sitting,K+1 ,0);

This attempt times out on almost all of the test input, only passing 8/30 cases.


Here is some code using an O(k) solution suggested by another user. Transferred to c++

    int seat1 = chairs[0], best = -1, accum = 1;
int unlucky[] = {0, -1};

for (int pos; pos < K + 1; pos++) {
    if (people[pos] <= seat1) {
        best = people[pos] + 1;
        accum -= 1;
    } else
        break;
}

if (accum < 0) {
    unlucky[0] = accum;
    unlucky[1] = 1;
}

int i = K, j = K - 1;
while (i >= 0 && people[i] > seat1) {
    if (chairs[j] >= people[i]) {
        accum += 1;
        j -= 1;
    } else {
        accum -= 1;
        i -= 1;
    }
    if (best == -1 && accum == 0) {
        best = i + 2;
    }
    if (accum < unlucky[0]) {
        unlucky[0] = accum;
        unlucky[1] = i + 2;
    }
}
fprintf(out_file, "%d\n%d", best, unlucky[1]);

First of all let's simplify this problem by thinking of the table as two circular buffers . One for the chairs and one for the people. We can say a buffer contains chairs 'C', people 'P' and empty spots 'E'.

So assuming we say that the circular buffer starts at 1, your example could be rewritten as

E C E E C E E C E E

E E P P E P E P E E

Your current solution would in the worst case be O(N*K) . Imagine a configuration like :

P P E E E E E E E

E E E E E E E E C

Your algorithm solves the above by shifting the people buffer seven times. And you implement a shift by going through all the people and shifting each individually. So your algorithm makes O(N) (size of desk) shifts and each shift involves moving all O(K) people by one spot. Thus O(N*K) . Though, this is I think a very pesimistic upper bound and you will be much faster on average.


I think a good O(K) could be implemented with the help of two pointers. A pointer into the people buffer and another pointer into the chair buffer. The idea is to go through the two buffers separately and match a waiting person with an available chair. The idea is that whenever you see chair, if there is a person waiting then you can match him with that chair. If there is no person waiting then you add the chair to the queue of available chairs. If there are available chairs and available persons after we have gone through both buffers then we know these are persons that would need to pass through the 1 point of the table to find a chair and we can match them up with the available chairs by matching the last waiting person with the first available chair.

In addition to the two pointers you can use two queues to keep track of available chairs and waiting persons. Below is pseudocode-ish version of the solution.

queue<Person> personQueue;
queue<Chair> chairQueue;
int cptr=0, pptr=0;

while(cptr < K || pptr < K+1) 
    if (cptr >= K) {
        //no chairs ahead of us only behind us
        personQueue.addAllRemainingPersons()
    }
    else if(pptr >= K+1 || chair[cptr] < person[pptr]) {
        // the currently pointed at chair is at a lower position 
        // than the pointed at person,
        // so we match this chair with a waiting person instead
        match(personQueue.back, chair[cptr]);
        cptr++;
    }
    else {
        //add person to the waiting-for-chair queue
        personQueue.push_back(person[pptr]);
        pptr++;
    }
}
// at this point we have a situation with many chair in front of many persons
// for example
// CCCCPPPPP
// This we solve by matching the last person with the first chair 
// until only one is left
while(!cQueue.empty()) {
    match(personQueue.back, chairQueue.front);
}

Determining who is the person who gets seated in the first chair can happen in the match function where you have enough information to be able to tell. The person who remains standing is the only one who hasn't found a seat (the only one for whom we will not call match).

To determine who will be in the first seat, we need to count seats/persons starting from the first seat in reverse direction and stop as soon as the number of persons is equal to the number of seats. To implement this we need an accumulator which is increased when a seat is encountered and decreased when a person is encountered. Stop when this accumulator is zero.

To determine who will be the person left standing up, we could just continue updating accumulator the same way and find the position where this accumulator gets its minimal value for the first time. For any person's position where this accumulator is not minimal (or when this minimum is closer to beginning of the array than some other minimum) we could find the position later in the array where this accumulator has the same value, which means there is enough place for this person to be seated.

See diagram below for more detailed explanation. It shows accumulator values for all possible seat/person positions. While algorithm traverses input arrays only once, this diagram shows accumulator values for these arrays traversed three times, so that cyclical nature of the problem is visible.

累加器值

Values for each arrays traversal are similar to each other. The only difference is that the values are decreased by one each time we come to the same point on the circle.

It is easy to notice that all accumulator values except one have the following property: we can always find another point with the same value further in the circle. This means the number of seats and the number of persons between these two points are equal to each other. This means each person with starting position between these two points will be seated also somewhere between these two points: all persons to the right are going away from this interval and do not pretend to take these seats. All persons to the left come too late.

Some interesting cases for interval between these equally-valued points are: (1) when interval crosses array boundary, (2) interval between two minimal values, (3) very short interval when empty seat is next to the person or if they are at the same point.

And only one position (4), corresponding to the last minimal accumulator's value (or first one if we search backwards) does not have this property. There is no point with the same value to the right. Person on this initial position will be left standing up.

Here is working code in Python: Ideone link .

def solve(k, s, p):
  seat1 = s[0]
  best = -1
  unlucky = (0, -1) # min(accum), position
  accum = 1

  # process all persons between start of the array and the seat #1 position
  for i, pos in enumerate(p):
    if pos <= seat1:
      best = i+1
      accum -= 1
    else:
      break

  if accum < 0:
    unlucky = (accum, 1)

  # process all seats/persons in reverse direction
  i = k
  j = k-1
  while i >= 0 and p[i] > seat1:
    if s[j] >= p[i]: # a seat
      accum += 1
      j -= 1
    else: # a person
      accum -= 1
      i -= 1

    if best == -1 and accum == 0:
      best = i+2 # +1 because indexing starts with 0 & +1 because of pre-decrement

    if accum < unlucky[0]:
      unlucky = (accum, i+2)

  return (best, unlucky[1])


print(solve(3, [2,5,8], [3,4,6,8]))

Since each of the seats/persons is inspected only once, time complexity is O(k).

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM