Program to determine total stops taken by elevator I was asked a question to write a optimal program that would determine the total number of stops a elevator has taken to serve X number of people.

There is a elevator in a building with M floors. This elevator can take a max of X people at a time or max of total weight Y. Given that a set of people has arrived and their weight and the floor they need to stop given how many stops has the elevator taken to serve all the people. Consider elevator serves in the first come first serve basis.

Example:

Let Array A be the weight of people to be considered A[] = {60, 80, 40 }.

Let Array B be the floors where person needs to be dropped respectively B[] = {2, 3, 5}.

Total building floors be 5,max allowed person in elevator be 2 at a time with max weight capacity being 200. For this example, the elevator would take total of 5 stops floors ground, 2, 3, ground, 5, ground.

What would be the optimal code for this? Is there any other better solutions or ways to improve my code?

class Solution
{
    /// &lt;summary&gt;
    /// Return total stops used
    /// &lt;/summary&gt;
    /// &lt;param name="A"&gt;weight of people&lt;/param&gt;
    /// &lt;param name="B"&gt;floors they need to get down&lt;/param&gt;
    /// &lt;param name="M"&gt;total floors in the building&lt;/param&gt;
    /// &lt;param name="X"&gt;Max people to carry at a time&lt;/param&gt;
    /// &lt;param name="Y"&gt;max weight to carry at a time&lt;/param&gt;
    /// &lt;returns&gt;&lt;/returns&gt;
    public int solution(int[] A, int[] B, int M, int X, int Y)
    {
        // initialize variables
        int totalStops = 0;
        long totalWeightPerRound = 0;
        int maxPersonsCount = 0;
        List&lt;int&gt; lstFloors = new List&lt;int&gt;();
        int currPerson = 0;
        bool startLift = false;
        while (currPerson &lt; A.Length)
        {
            //Should current person be considered?
            if ((totalWeightPerRound + A[currPerson]) &lt;= Y &amp;&amp; (maxPersonsCount+1) &lt;= X)
            {
                totalWeightPerRound += A[currPerson];
                maxPersonsCount++;
                lstFloors.Add(B[currPerson]);
                //If curr person is last person then start the lift
                if (currPerson == A.Length - 1)
                    startLift = true;

                currPerson++;
            }
            else
            {
                startLift = true;
            }

            if (startLift)
            {
                totalStops += lstFloors.Distinct().Count() + 1;
                //reset variable
                lstFloors.Clear();
                maxPersonsCount = 0;
                totalWeightPerRound = 0;
                startLift = false;
            }
        }

        return totalStops;
    }
}
So you said that the elevator works with the first come first serve basis. That screams like using a Queue. Moreover i like using classes as much as possible because i feel that implementing a solution becomes very easy because the abstraction is more on a human level and the readability increases.
I demonstrate it on my code.



So first you know that you have Persons who want to drive from ground to a Target Floor. A Person also have a Weight. So i have implemented this in a class:

public class Person
{
    public int Weight { get; set; }

    public int ActualFloor { get; set; }

    public int TargetFloor { get; set; }

    public Person(int weight, int targetFloor, int actualFloor = 0)
    {
        this.Weight = weight;
        this.TargetFloor = targetFloor;
        this.ActualFloor = actualFloor;
    }
}




Naming:
Moreover you know that the Elevator has different attributes too. You have implemented them in Y for Maximum Weight and X for the Maximum Number of People and i think that X and Y are really bad names for MaxWeight and MaxPeople. Why not name them what they are? That makes it easier for you as the coder and easier for other coworkers trying to understand your code.

My Elevator Properties and the Constructor are implemented as this:

public class Elevator
{
    public readonly int MaxWeight;

    public readonly int MaxPersons;

    public readonly int MaxFloors;

    public Queue&lt;Person&gt; Persons { get; set; }

    private List&lt;Person&gt; Passengers { get; set; }

    public Elevator(int maxFloors, int maxWeight, int maxPersons)
    {
        this.MaxFloors = maxFloors;
        this.MaxPersons = maxPersons;
        this.MaxWeight = maxWeight;
        this.Persons = new Queue&lt;Person&gt;();
        this.Passengers = new List&lt;Person&gt;();
    }
}


MaxWeight, MaxPersons and MaxFloors should be clear. The Queue Persons contains the People you want to drive around. Passengers are the People which are in this cycle in the elevator.

Now you can calculate the number of stops very easy:

public int CalculateNumberOfStops()
{
    int stops = 0;

    while (!this.Persons.IsEmpty())
    {
        while (this.IsOneMorePersonFitting())
        {
            this.Passengers.Add(this.Persons.Dequeue());
        }

        stops += this.Passengers.GroupBy(x =&gt; x.TargetFloor).Count();
        this.Passengers.Clear();

        // Drive to Floor Zero
        stops++;
    }

    return stops;
}

private bool IsOneMorePersonFitting()
{
    return !this.Persons.IsEmpty() &amp;&amp;
           this.Passengers.Sum(x =&gt; x.Weight) + this.Persons.Peek().Weight &lt; this.MaxWeight &amp;&amp;
           this.Passengers.Count &lt; this.MaxPersons;
}


I am shifting the People from the Queue into the List as long the cap of the elevator is not full and the Queue is not empty. 
The number of stops are calculated with the number of Groups in the List. If you have 2 Passengers and Pass 1 wants to Floor 1 and Pass 2 wants to Floor 2 then you will have two groups. But if both want to the same Floor then you will only have one group. 



You can add persons and error check with a helper function like this:

public void AddPersons(IEnumerable&lt;Person&gt; persons)
{
    foreach (var person in persons)
    {
        // Check if person is valid:
        if (person.TargetFloor &gt; this.MaxFloors ||
            person.Weight &gt; this.MaxWeight)
        {
            throw new ArgumentException();
        }

        this.Persons.Enqueue(person);
    }
}


I extended the Methods of Queue with the IsEmpty function to make is a little easier to read:

public static class ExtensionHelper
{
    public static bool IsEmpty&lt;T&gt;(this Queue&lt;T&gt; queue)
    {
        return queue.Count == 0;
    }
}




Using the elevator:

static void Main(string[] args)
{
    var elevator = new Elevator(5, 200, 2);
    elevator.AddPersons(new[] { new Person(60, 2), new Person(80, 3), new Person(40, 5) });
    var stops = elevator.CalculateNumberOfStops();

    Console.WriteLine(stops);
    Console.ReadKey();
}




For better readability you could define an empty constructor and then use it as

var elevator = new Elevator() { MaxWeight = 200, MaxPersons = 2 };


but then you can't use a readonly field and the programmer must know what is needed for the instance.