A logistics company needs to construct a logistics transfer center in a certain area. The company has identified 5 candidate locations for the transfer centers, which will need to deliver goods to 10 subordinate outlets. The distance matrix between the 5 candidate locations and the 10 outlets is as follows:

| Candidate Location \ Outlet | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 |
|------------------------------|----------|----------|----------|----------|----------|----------|----------|----------|----------|-----------|
| 1                            | 15       | 22       | 30       | 45       | 50       | 12       | 28       | 35       | 40       | 25        |
| 2                            | 10       | 18       | 25       | 35       | 40       | 8        | 20       | 28       | 32       | 18        |
| 3                            | 25       | 30       | 35       | 50       | 55       | 22       | 32       | 40       | 45       | 30        |
| 4                            | 30       | 35       | 40       | 55       | 60       | 25       | 35       | 45       | 50       | 35        |
| 5                            | 5        | 12       | 20       | 30       | 35       | 5        | 15       | 22       | 28       | 12        |

If 2 out of the 5 candidate locations are to be selected to build transfer centers, what is the minimum possible value of the maximum distance from all outlets to their nearest transfer center?

Problem type: ILP (Medium: 56 variables, 111 constraints, 255 nonzeros)

Mathematical model:
Set:
1. CandidateLocation
The set of candidate locations, \( L = \{1, 2, \dots, CandidateLocationNum\} \)
2. Outlet
The set of outlets, \( O = \{1, 2, \dots, OutletNum\} \)

Parameter:
1. CandidateLocationNum
5
2. OutletNum
10
3. Distance
[[15, 22, 30, 45, 50, 12, 28, 35, 40, 25], [10, 18, 25, 35, 40, 8, 20, 28, 32, 18], [25, 30, 35, 50, 55, 22, 32, 40, 45, 30], [30, 35, 40, 55, 60, 25, 35, 45, 50, 35], [5 ,12 ,20 ,30 ,35 ,5 ,15 ,22 ,28 ,12]]
4. SeletLocationNum
2

Decision variable:
1. SeletLocation
Binary variable, \( x_{i} \) for all \( i \in L \), representing whether candidate location \( i \) is selected (1 if selected, 0 otherwise)
2. Auxiliary variable for min-max obj
Integer variable, \( z \) representing the maximum distance from any outlet to its nearest transfer center.
3. Auxiliary variables to track relationships between outlets and candidate locations
Binary variable, \(y_{ij}\) for all \( i \in L, j \in O \), representing whether outlet \( j \) is served by candidate location \( i \) (1 if covered, 0 otherwise).

Objective:
The minimum possible value of the maximum distance from any outlets to its nearest transfer center.
min: z

Constraint:
1. Location selection
\sum_{i\in L} x_{i} = SeletLocationNum
2. Outlet assignment 1
\sum_{i\in L} y_{ij} = 1, \forall j \in O
3. Outlet assignment 2
y_{ij} \leq x_{i}, \forall i \in L, j \in O
4. Max distance
z \geq Distance_{ij}*y_{ij}, \forall i \in L, j \in O

Type:
Integer, Linear, Linear
ILP

Gurobi implementation:
import gurobipy as gp
from gurobipy import GRB


def solve_p_center(
    distance=[
        [15, 22, 30, 45, 50, 12, 28, 35, 40, 25],
        [10, 18, 25, 35, 40, 8, 20, 28, 32, 18],
        [25, 30, 35, 50, 55, 22, 32, 40, 45, 30],
        [30, 35, 40, 55, 60, 25, 35, 45, 50, 35],
        [5, 12, 20, 30, 35, 5, 15, 22, 28, 12]
    ],
    select_location_num=2
):
    """
    Models and solves the P-Center facility location problem.
    """
    # --- 1. Model Creation ---
    model = gp.Model("P-Center-Problem")

    # --- 2. Sets ---
    # Derive sets from the dimensions of the input data
    candidate_locations = range(len(distance))
    outlets = range(len(distance[0]))

    # --- 3. Decision Variables ---
    # x[i] = 1 if location i is selected, 0 otherwise
    x = model.addVars(candidate_locations, vtype=GRB.BINARY, name="select_location")

    # z = auxiliary variable for the min-max objective (maximum distance)
    z = model.addVar(vtype=GRB.CONTINUOUS, name="max_distance")

    # y[i, j] = 1 if outlet j is assigned to location i, 0 otherwise
    y = model.addVars(candidate_locations, outlets, vtype=GRB.BINARY, name="assign")

    # --- 4. Objective Function ---
    # Minimize the maximum distance (z)
    model.setObjective(z, GRB.MINIMIZE)

    # --- 5. Constraints ---
    # Constraint 1: Select exactly 'p' (select_location_num) locations
    model.addConstr(gp.quicksum(x[i] for i in candidate_locations) == select_location_num,
                    name="p_selection")

    # Constraint 2: Each outlet must be assigned to exactly one location
    for j in outlets:
        model.addConstr(gp.quicksum(y[i, j] for i in candidate_locations) == 1,
                        name=f"outlet_assignment_{j}")

    # Constraint 3: An outlet can only be assigned to a selected (opened) location
    for i in candidate_locations:
        for j in outlets:
            model.addConstr(y[i, j] <= x[i], name=f"valid_assignment_{i}_{j}")

    # Constraint 4: Define the maximum distance 'z'
    # If outlet j is assigned to location i (y[i,j]=1), then z must be at least
    # as large as the distance between them.
    for i in candidate_locations:
        for j in outlets:
            model.addConstr(z >= distance[i][j] * y[i, j],
                            name=f"max_distance_link_{i}_{j}")

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

    # --- 7. 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_p_center()
    print(result)


Solution status: OPTIMAL
Optimal objective value: 35