Skip to content

Commit d49ecf8

Browse files
authored
Merge pull request #179 from BrianLusina/feat/algorithms-matrices-meeting-point
feat(algorithms, matrices): meeting point
2 parents a5c09c4 + fd3a00a commit d49ecf8

23 files changed

+205
-6
lines changed

DIRECTORY.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -260,6 +260,8 @@
260260
* Josephus Circle
261261
* [Test Josephus Circle](https://github.com/BrianLusina/PythonSnips/blob/master/algorithms/josephus_circle/test_josephus_circle.py)
262262
* Matrix
263+
* Best Meeting Point
264+
* [Test Best Meeting Point](https://github.com/BrianLusina/PythonSnips/blob/master/algorithms/matrix/best_meeting_point/test_best_meeting_point.py)
263265
* Isvalidsudoku
264266
* [Test Is Valid Sudoku](https://github.com/BrianLusina/PythonSnips/blob/master/algorithms/matrix/isvalidsudoku/test_is_valid_sudoku.py)
265267
* Memoization

algorithms/dynamic_programming/min_path_sum/__init__.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -71,13 +71,13 @@ def min_path_sum_grid(grid: List[List[int]]) -> int:
7171
for i in range(m):
7272
for j in range(n):
7373
if i == 0 and j > 0:
74-
# First row but not [0][0]: we can only come from the left
74+
# First row but not [0][0]: we can only come from the left
7575
grid[i][j] += grid[i][j - 1]
7676
elif j == 0 and i > 0:
7777
# First column but not [0][0]: we can only come from above
7878
grid[i][j] += grid[i - 1][j]
7979
elif i > 0 and j > 0:
80-
# For all other cells, choose the minimum of:
80+
# For all other cells, choose the minimum of:
8181
# - the path sum from above (i-1, j)
8282
# - the path sum from the left (i, j-1)
8383
grid[i][j] += min(grid[i - 1][j], grid[i][j - 1])

algorithms/dynamic_programming/min_path_sum/test_min_path_sum.py

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,12 @@
22
import copy
33
from typing import List
44
from parameterized import parameterized
5-
from algorithms.dynamic_programming.min_path_sum import min_path_sum_in_triangle, min_path_sum_in_triangle_2, min_path_sum_grid, min_path_sum_grid_2
5+
from algorithms.dynamic_programming.min_path_sum import (
6+
min_path_sum_in_triangle,
7+
min_path_sum_in_triangle_2,
8+
min_path_sum_grid,
9+
min_path_sum_grid_2,
10+
)
611

712
MIN_PATH_SUM_TRIANGLE_TEST_CASES = [
813
([[5]], 5),
@@ -13,8 +18,8 @@
1318
]
1419

1520
MIN_PATH_SUM_GRID_TEST_CASES = [
16-
([[1,3,1],[1,5,1],[4,2,1]], 7),
17-
([[1,2,3],[4,5,6]], 12),
21+
([[1, 3, 1], [1, 5, 1], [4, 2, 1]], 7),
22+
([[1, 2, 3], [4, 5, 6]], 12),
1823
([[1, 2, 5], [3, 2, 1]], 6),
1924
([[5, 9, 1, 3], [4, 2, 1, 7], [3, 1, 1, 2]], 15),
2025
([[5]], 5),

algorithms/intervals/min_intervals_for_queries/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33

44

55
def min_interval(intervals: List[List[int]], queries: List[int]) -> List[int]:
6-
query_len=len(queries)
6+
query_len = len(queries)
77

88
query_indexes = list(range(query_len))
99
query_indexes.sort(key=lambda q: queries[q])
Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
# Best Meeting Point
2+
3+
You are given a 2D grid of size m×n, where each cell contains either a 0 or a 1. A 1 represents the home of a friend,
4+
and a 0 represents an empty space.
5+
6+
Your task is to return the minimum total travel distance to a meeting point. The total travel distance is the sum of the
7+
Manhattan distances between each friend’s home and the meeting point.
8+
9+
The **Manhattan Distance** between two points `(x1, y1)` and `(x2, y2)` is calculated as:
10+
`|x2 - x1| + |y2 - y1|`.
11+
12+
## Constraints
13+
14+
- m == grid.length
15+
- n == grid[i].length
16+
- 1 ≤ m, n ≤ 50
17+
- `grid[i][j]` is either 0 or 1.
18+
- There will be at least two friends in the grid.
19+
20+
## Examples
21+
22+
![Example 1](./images/examples/best_meeting_point_example_1.png)
23+
![Example 2](./images/examples/best_meeting_point_example_2.png)
24+
![Example 3](./images/examples/best_meeting_point_example_3.png)
25+
26+
## Solution
27+
28+
The main idea of this algorithm is that the total Manhattan distance is minimized when all friends meet at the median
29+
position, calculated separately for rows and columns. As Manhattan distance can be split into vertical and horizontal
30+
components, we collect all the row indices and column indices of the friends and compute the distance to their respective
31+
medians. As we loop through the grid row-wise and column-wise, the row and column indices are gathered in sorted order
32+
naturally, so no additional sorting is needed. Finally, a two-pointer approach is used to efficiently compute the total
33+
distance by pairing positions from both ends toward the center.
34+
35+
Using the intuition above, we implement the algorithm as follows:
36+
37+
1. Create two vectors, `rows` and `cols`, to store all cells’ row and column indexes where `grid[i][j] == 1`.
38+
2. Iterate through the grid row by row. For each cell that contains a 1, push the row index `i` into the `rows` vector.
39+
3. Iterate through the grid column by column. For each cell that contains a `1`, push the column index j into the `cols` vector.
40+
4. Use the helper function getMinDistance(rows) to calculate the total vertical distance to the optimal row (median).
41+
5. Use the helper function getMinDistance(cols) to calculate the total horizontal distance to the optimal column (median).
42+
6. Return the sum of the two distances as the minimum total travel distance.
43+
44+
The getMinDistance helper function receives a list of positions, points, and returns the total minimum travel distance
45+
to the median. The points list contains either row or column indices of friends. As the Manhattan distance is minimized
46+
at the median, it uses a two-pointer technique as follows:
47+
48+
- Initialize a variable, distance, with 0 to compute the total distance.
49+
- Initialize two pointers, i and j, one at the start and the other at the end.
50+
- Each step adds the difference points[j] - points[i] to the total distance.
51+
- This process continues until the pointers meet.
52+
- Returns the total computed distance.
53+
54+
![Solution 1](./images/solutions/best_meeting_point_solution_1.png)
55+
![Solution 2](./images/solutions/best_meeting_point_solution_2.png)
56+
![Solution 3](./images/solutions/best_meeting_point_solution_3.png)
57+
![Solution 4](./images/solutions/best_meeting_point_solution_4.png)
58+
![Solution 5](./images/solutions/best_meeting_point_solution_5.png)
59+
![Solution 6](./images/solutions/best_meeting_point_solution_6.png)
60+
![Solution 7](./images/solutions/best_meeting_point_solution_7.png)
61+
![Solution 8](./images/solutions/best_meeting_point_solution_8.png)
62+
![Solution 9](./images/solutions/best_meeting_point_solution_9.png)
63+
![Solution 10](./images/solutions/best_meeting_point_solution_10.png)
64+
![Solution 11](./images/solutions/best_meeting_point_solution_11.png)
65+
![Solution 12](./images/solutions/best_meeting_point_solution_12.png)
66+
![Solution 13](./images/solutions/best_meeting_point_solution_13.png)
67+
68+
### Time Complexity
69+
70+
The time complexity of the above algorithm is `O(m×n+k)`, where m×n are the dimensions of the grid and k is the number
71+
of friends (number of 1s in the grid). This is because:
72+
73+
- O(m×n) to traverse the entire grid and collect row and column indices.
74+
- O(k) to compute distances in getMinDistance, where k is the number of friends.
75+
76+
### Space Complexity
77+
78+
The algorithm’s space complexity is `O(k)` because we store up to k row indices and k column indices in two separate
79+
vectors.
Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
from typing import List
2+
3+
4+
def min_total_distance(grid: List[List[int]]) -> int:
5+
rows, cols = [], []
6+
7+
# Helper function to calculate total distance to the median
8+
def get_min_distance(points: List[int]) -> int:
9+
distance = 0
10+
i, j = 0, len(points) - 1
11+
12+
# Use two pointers to accumulate distance from both ends toward the center
13+
while i < j:
14+
distance += points[j] - points[i]
15+
i += 1
16+
j -= 1
17+
18+
return distance
19+
20+
# Collect all row indices where grid[i][j] == 1
21+
for i in range(len(grid)):
22+
for j in range(len(grid[0])):
23+
if grid[i][j] == 1:
24+
rows.append(i)
25+
26+
# Collect all column indices where grid[i][j] == 1
27+
for j in range(len(grid[0])):
28+
for i in range(len(grid)):
29+
if grid[i][j] == 1:
30+
cols.append(j)
31+
32+
# Compute total vertical and horizontal distances to medians
33+
return get_min_distance(rows) + get_min_distance(cols)
34+
35+
36+
def min_total_distance_2(grid: List[List[int]]) -> int:
37+
"""
38+
Find the minimum total distance for all people to meet at one point.
39+
The optimal meeting point is the median of all x-coordinates and y-coordinates.
40+
41+
Args:
42+
grid: 2D grid where 1 represents a person's location, 0 represents empty space
43+
44+
Returns:
45+
Minimum total Manhattan distance for all people to meet
46+
"""
47+
48+
def calculate_distance_sum(positions: List[int], meeting_point: int) -> int:
49+
"""
50+
Calculate sum of distances from all positions to the meeting point.
51+
52+
Args:
53+
positions: List of coordinate values (either row or column indices)
54+
meeting_point: The target coordinate to measure distance to
55+
56+
Returns:
57+
Sum of absolute distances
58+
"""
59+
return sum(abs(position - meeting_point) for position in positions)
60+
61+
# Collect all row and column indices where people are located
62+
row_indices = []
63+
column_indices = []
64+
65+
for row_index, row in enumerate(grid):
66+
for column_index, cell_value in enumerate(row):
67+
if cell_value == 1: # Person found at this location
68+
row_indices.append(row_index)
69+
column_indices.append(column_index)
70+
71+
# Sort column indices to find median (row indices already sorted due to iteration order)
72+
column_indices.sort()
73+
74+
# Find median positions (optimal meeting point)
75+
# Using bit shift for integer division by 2
76+
median_row = row_indices[len(row_indices) >> 1]
77+
median_column = column_indices[len(column_indices) >> 1]
78+
79+
# Calculate total distance as sum of row distances and column distances
80+
total_distance = calculate_distance_sum(
81+
row_indices, median_row
82+
) + calculate_distance_sum(column_indices, median_column)
83+
84+
return total_distance
31.7 KB
Loading
27 KB
Loading
32.7 KB
Loading
29.3 KB
Loading

0 commit comments

Comments
 (0)