On a campus represented as a 2D grid, there are n workers and m bikes, with n <= m. You are given a list of the workers' locations: workers[i] = (xi, yi) and a list of bike locations: bikes[j] = (xj, yj). All the workers and bikes are distinct.
Assign a bike to each worker such that the sum of the Manhattan distances between each worker and their assigned bike is minimized.
The Manhattan distance between two points (a, b) and (c, d) is |a - c| + |b - d|.
Return the minimum possible sum of Manhattan distances between each worker and their assigned bike.
Example 1:
Input: workers = [[0,0],[2,1]], bikes = [[1,2],[3,3]] Output: 6 Explanation: Assign worker[0] to bike[0], worker[1] to bike[1]. The Manhattan distance of worker[0] to bike[0] is |0-1| + |0-2| = 3. The Manhattan distance of worker[1] to bike[1] is |2-3| + |1-3| = 3. The total Manhattan distance is 3 + 3 = 6.
Example 2:
Input: workers = [[0,0],[1,1],[2,0]], bikes = [[1,0],[2,2],[2,1]] Output: 4 Explanation: Assign worker[0] to bike[0], worker[1] to bike[1], worker[2] to bike[2]. The Manhattan distance of worker[0] to bike[0] is |0-1| + |0-0| = 1. The Manhattan distance of worker[1] to bike[1] is |1-2| + |1-2| = 2. The Manhattan distance of worker[2] to bike[2] is |2-2| + |0-1| = 1. The total Manhattan distance is 1 + 2 + 1 = 4.
Constraints:
n == workers.lengthm == bikes.length1 <= n <= m <= 10workers[i].length == bikes[j].length == 20 <= xi, yi < 10000 <= xj, yj < 1000When you get asked this question in a real-life environment, it will often be ambiguous (especially at FAANG). Make sure to ask these questions in that case:
The problem wants us to find the best way to assign bikes to workers. The brute force way is to just try out every possible matching between workers and bikes and see which assignment is the best.
Here's how the algorithm would work step-by-step:
def campus_bikes_brute_force(workers, bikes):
minimum_distance = float('inf')
def calculate_distance(worker_index, assigned_bikes, current_distance):
nonlocal minimum_distance
# Base case: all workers have been assigned a bike
if worker_index == len(workers):
minimum_distance = min(minimum_distance, current_distance)
return
# Try assigning each unassigned bike to the current worker
for bike_index in range(len(bikes)):
if bike_index not in assigned_bikes:
# Calculate distance between the current worker and bike
distance = abs(workers[worker_index][0] - bikes[bike_index][0]) + \
abs(workers[worker_index][1] - bikes[bike_index][1])
# Recursively call function for next worker
calculate_distance(
worker_index + 1,
assigned_bikes | {bike_index},
current_distance + distance
)
# Initialize the recursive calls with initial values
calculate_distance(0, set(), 0)
# After trying every possible combination, return the minimum distance
return minimum_distanceThe core idea is to explore possible assignments of workers to bikes and to do so in a way that avoids redundant calculations. We will use a technique to only investigate the most promising assignments and discard less promising ones early on to dramatically reduce the overall computation.
Here's how the algorithm would work step-by-step:
def campus_bikes_two(workers, bikes):
def calculate_distance(worker, bike):
return abs(worker[0] - bike[0]) + abs(worker[1] - bike[1])
number_of_workers = len(workers)
number_of_bikes = len(bikes)
# Initialize the best total distance found so far to infinity.
minimum_total_distance = float('inf')
def find_minimum_distance(worker_index, assigned_bikes, current_total_distance):
nonlocal minimum_total_distance
# If we've assigned all workers to bikes,
if worker_index == number_of_workers:
# update the minimum total distance if necessary.
minimum_total_distance = min(minimum_total_distance, current_total_distance)
return
# If the current total distance is already greater than the minimum,
# it's impossible to find a better solution from this point forward.
if current_total_distance > minimum_total_distance:
return
for bike_index in range(number_of_bikes):
# Only consider bikes that haven't been assigned yet.
if bike_index not in assigned_bikes:
distance = calculate_distance(workers[worker_index], bikes[bike_index])
# Recursively explore assigning the current worker to the current bike.
find_minimum_distance(
worker_index + 1,
assigned_bikes | {bike_index},
current_total_distance + distance,
)
# Start the process by assigning the first worker to bikes.
find_minimum_distance(0, set(), 0)
return minimum_total_distance| Case | How to Handle |
|---|---|
| Empty workers or bikes array | Return 0 since no assignments are possible if either workers or bikes are empty. |
| Workers and bikes arrays have significantly different lengths (e.g., one worker and many bikes) | The cost calculation and minimization should still function correctly, exploring all possible bike assignments to the single worker. |
| Large input size approaching the memory limit for recursive solutions | Consider using dynamic programming with memoization instead of recursion to avoid stack overflow errors and improve memory usage. |
| All workers are at the same location or all bikes are at the same location | The distance calculation should still function correctly, resulting in either 0 or the distance to the nearest other location. |
| Integer overflow when calculating Manhattan distance with large coordinate values | Use a data type like long to store the Manhattan distance to prevent integer overflow. |
| Workers or bikes have negative coordinate values | The Manhattan distance calculation should handle negative coordinates correctly as the absolute difference is taken. |
| Coordinate values are zero | The distance calculation should handle zero coordinates correctly as the absolute difference is taken. |
| Duplicate worker or bike locations | The algorithm should correctly handle duplicate locations by exploring all possible assignments, not treating them as a single entity. |