An airport has two available gates (G1, G2) and needs to assign three flights (F1: 10:00-11:00, F2: 10:30-11:30, F3: 11:15-12:30, F4: 10:45-11:45) to these gates. Flights assigned to the same gate must have non-overlapping time intervals, and the goal is to minimize flight delays, where each 1-minute delay beyond the scheduled departure time incurs a cost of 1 unit. How should the flights be assigned to the gates to achieve this objective?

Problem type: ILP (Small: 12 variables, 20 constraints, 60 nonzeros)

Mathematical model:
Set:
1. Gates
The set of gates available at the airport, \( G = \{1, 2\} \)
2. Flights
The set of flights to be assigned to the gates, \( F = \{1, 2, 3, 4\} \)

Parameter:
1. ArrangeStartTime
[600, 630, 645, 675]
2. Duration
[60, 60, 75, 60]
3. CostPerDelyTime
1
4. M: A large positive number
1500

Decision variable:
1. Assignment
Binary variable \( x_{fg} \in \{0, 1\} \) for all \( f \in F, g \in G \), where \( x_{fg} = 1 \) if flight \( f \) is assigned to gate \( g \), and \( x_{fg} = 0 \) otherwise.
2. ActualStartTime
Integer variable \( s_f \) representing the actual start time of flight \( f \in F\)

Objective:
The total cost of flight delays
min: \sum_{f\in F}\sum_{g\in G}CostPerDelyTime\cdot (s_f-ArrangeStartTime_f)\cdot x_{fg}

Constraint:
1. Actual start time should be greater than or equal to the scheduled start time.
s_f \geq ArrangeStartTime_f, \forall f \in F

2. Every flight should be assigned to exactly one gate.
\sum_{g\in G}x_{fg} = 1, \forall f \in F

3. The actual start time should be greater than or equal to the end time of the previous flight at the same gate.
s_{f2} \geq s_{f1}+Duration_{f1} - M*(2-x[f1, g]-x[f2, g]), \forall f1, f2 \in F, f1 < f2, x_{f1g} = 1, x_{f2g} = 1 \forall g \in G

Type:
Integer, Linear, Linear
ILP

Gurobi implementation:
import gurobipy as gp
from gurobipy import GRB


def solve_flight_gate_assignment(
    gates=[1, 2],
    flights=[1, 2, 3, 4],
    arrange_start_time={1: 600, 2: 630, 4: 645, 3: 675},
    duration={1: 60, 2: 60, 3: 75, 4: 60},
    cost_per_delay=1,
    big_m=1500,
):
    """
    Models and solves the flight gate assignment problem to minimize total delay.
    """
    # --- 1. Model Creation ---
    model = gp.Model("FlightGateAssignment")

    # --- 2. Decision Variables ---
    # x[f,g] = 1 if flight f is assigned to gate g, else 0
    x = model.addVars([(f, g) for f in flights for g in gates], vtype=GRB.BINARY, name="x")

    # s[f] = actual start time of flight f
    s = model.addVars(flights, vtype=GRB.INTEGER, name="s")

    # --- 3. Objective Function ---
    # Minimize total delay cost
    obj = gp.quicksum(cost_per_delay * (s[f] - arrange_start_time[f])
                      for f in flights)
    model.setObjective(obj, GRB.MINIMIZE)

    # --- 4. Constraints ---
    # Constraint 1: Actual start time should be greater than or equal to the scheduled start time
    for f in flights:
        model.addConstr(s[f] >= arrange_start_time[f], f"start_time_constraint_{f}")

    # Constraint 2: Every flight should be assigned to exactly one gate
    for f in flights:
        model.addConstr(gp.quicksum(x[f, g] for g in gates) == 1, f"one_gate_per_flight_{f}")

    # Constraint 3: The actual start time should be greater than or equal to the end time
    # of the previous flight at the same gate (linearized using Big-M).
    # This formulation imposes a fixed sequence f1 -> f2 if f1 < f2.
    for g in gates:
        for f1 in flights:
            for f2 in flights:
                if f1 < f2:  # for flights where f1 comes before f2
                    # If both flights are assigned to the same gate g,
                    # then f2 must start after f1 ends
                    model.addConstr(
                        s[f2] >= s[f1] + duration[f1] - big_m * (2 - x[f1, g] - x[f2, g]),
                        f"sequence_constraint_{f1}_{f2}_{g}",
                    )

    # --- 5. Solve the Model ---
    # model.setParam("OutputFlag", 0) # Suppress Gurobi output
    model.optimize()

    # --- 6. Return Results ---
    if model.status == GRB.OPTIMAL:
        return {"status": "optimal", "obj": model.ObjVal}
    else:
        return {"status": f"{model.status}"}


# Run the solver function
if __name__ == "__main__":
    result = solve_flight_gate_assignment()
    print(result)

Solution status: OPTIMAL
Optimal objective value: 30