Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions DIRECTORY.md
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,8 @@
* Frog Position After T Seconds
* [Test Frog Position After T Seconds](https://github.com/BrianLusina/PythonSnips/blob/master/algorithms/graphs/frog_position_after_t_seconds/test_frog_position_after_t_seconds.py)
* Greedy
* Boats
* [Test Boats To Save People](https://github.com/BrianLusina/PythonSnips/blob/master/algorithms/greedy/boats/test_boats_to_save_people.py)
* Gas Stations
* [Test Gas Stations](https://github.com/BrianLusina/PythonSnips/blob/master/algorithms/greedy/gas_stations/test_gas_stations.py)
* Jump Game
Expand All @@ -91,6 +93,8 @@
* [Test Majority Element](https://github.com/BrianLusina/PythonSnips/blob/master/algorithms/greedy/majority_element/test_majority_element.py)
* Min Arrows
* [Test Find Min Arrows](https://github.com/BrianLusina/PythonSnips/blob/master/algorithms/greedy/min_arrows/test_find_min_arrows.py)
* Spread Stones
* [Test Minimum Moves To Spread Stones](https://github.com/BrianLusina/PythonSnips/blob/master/algorithms/greedy/spread_stones/test_minimum_moves_to_spread_stones.py)
* Huffman
* [Decoding](https://github.com/BrianLusina/PythonSnips/blob/master/algorithms/huffman/decoding.py)
* [Encoding](https://github.com/BrianLusina/PythonSnips/blob/master/algorithms/huffman/encoding.py)
Expand Down
97 changes: 97 additions & 0 deletions algorithms/greedy/boats/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
# Boats to save people

A big ship with numerous passengers is sinking, and there is a need to evacuate these people with the minimum number of
life-saving boats. Each boat can carry, at most, two persons however, the weight of the people cannot exceed the
carrying weight limit of the boat.

We are given an array, people, where people[i] is the weight of the `ith` person, and an infinite number of boats, where
each boat can carry a maximum weight, limit. Each boat carries, at most, two people at the same time. This is provided
that the sum of the weight of these people is under or equal to the weight limit.

You need to return the minimum number of boats to carry all persons in the array.

## Constraints

- 1 <= `people.length` <= 5 * 10^3
- 1 <= `people[i]` <= `limit` <= 3 * 10^3

## Examples

![Example 1](./images/examples/boats_to_save_people_example_1.png)
![Example 2](./images/examples/boats_to_save_people_example_2.png)
![Example 3](./images/examples/boats_to_save_people_example_3.png)
![Example 4](./images/examples/boats_to_save_people_example_4.png)
![Example 5](./images/examples/boats_to_save_people_example_5.png)

## Solution

1. [Naive Approach](#naive-approach)
2. [Optimized Approach Using Greedy Pattern](#greedy-pattern)

### Naive Approach

The naive approach is to use a nested loop. For each person, we can check all the remaining people to see if they can
form a pair that fits into a boat. If we find a pair, we’ll remove them from the array, increment the number of boats
used, and move to the next person. If we can’t find a pair for a person, we put them in a boat alone and increment the
number of boats used. We repeat this process until all people are rescued.

The time complexity of this approach is O(n^2), since we’ll use the nested loop to make pairs.

### Greedy Pattern

To solve the problem, we can use the greedy pattern and pair people with the lightest and heaviest people available, as
long as their combined weight does not exceed the weight limit. If the combined weight exceeds the limit, we can only
send one person on that boat. This approach ensures that we use the minimum number of boats to rescue the people.

The steps to implement the approach above are given below:

1. Sort the people array in ascending order so that the lightest person is at the start of the array, and the heaviest
person is at the end.

2. Initialize two pointers, left and right. The left pointer points to the lightest person at the start of the array,
and the right pointer points to the heaviest person at the end of the array. Next, a variable, boats, is initialized
to 0, representing the number of boats used.

3. Iterate over the people array until the left pointer is greater than the right pointer. This means that all people
have been rescued. Perform the following steps in each iteration of the loop
- Check if both the lightest and heaviest persons can fit in one boat, i.e., people[left] + people[right] is less
than or equal to limit. If they can fit, the left pointer is incremented and the right pointer is decremented.
- If they cannot fit in one boat, the heaviest person is rescued alone, and the right pointer is decremented.
- The boats variable is incremented by 1, representing the number of boats used.

4. Return the minimum number of boats required to rescue all the people.

![Solution 1](./images/solutions/boats_to_save_people_solution_1.png)
![Solution 2](./images/solutions/boats_to_save_people_solution_2.png)
![Solution 3](./images/solutions/boats_to_save_people_solution_3.png)
![Solution 4](./images/solutions/boats_to_save_people_solution_4.png)
![Solution 5](./images/solutions/boats_to_save_people_solution_5.png)
![Solution 6](./images/solutions/boats_to_save_people_solution_6.png)
![Solution 7](./images/solutions/boats_to_save_people_solution_7.png)
![Solution 8](./images/solutions/boats_to_save_people_solution_8.png)
![Solution 9](./images/solutions/boats_to_save_people_solution_9.png)
![Solution 10](./images/solutions/boats_to_save_people_solution_10.png)

#### Solution Summary

- Sort the people array.
- Initialize two pointers—left at the start and right at the end of the array.
- Iterate over the people array while the left pointer is less than or equal to the right pointer.
- Check if both the lightest and heaviest persons can fit in one boat. If so, increment the left pointer and decrement
the right pointer.
- Otherwise, rescue the heaviest person alone and decrement the right pointer.
- Increment the boats after each rescue operation.

#### Time Complexity

The time complexity for the solution is O(n log n), since sorting the people array takes O(n log n) time.

#### Space Complexity

The sorting algorithm takes O(n) space to sort the people array. Therefore, the space complexity of the solution above
is O(n).

## Topics

- Greedy
- Two Pointers
41 changes: 41 additions & 0 deletions algorithms/greedy/boats/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
from typing import List


def rescue_boats(people: List[int], limit: int) -> int:
"""
Finds the minimum number of rescue boats that can be used to save people from a sinking ship. Note that the assumption
made here is that the number of boats is unlimited
Args:
people (list): list of weights of people
limit (int): weight limit of each boat
Returns:
int: minimum number of rescue boats required
"""
# copy over the people list to avoid mutating the input list
weights = people[:]
# sort the list of weights
weights.sort()

# using two pointers to move along the weights to be able to track pairs of people, the left pointer will be at 0
# initially, i.e. at the lighter person and the right pointer will be at the end of the list, which will be the heavier
# person
left_pointer, right_pointer = 0, len(weights) - 1

# this keeps track of the total rescue boats that will be used
boats = 0

while left_pointer <= right_pointer:
lightest = weights[left_pointer]
heaviest = weights[right_pointer]
current_weight = lightest + heaviest
# If the current weight is less than the limit of a single boat
if current_weight <= limit:
# move to the next heavier person and back down the heavier scale
left_pointer += 1
# the heavier person boards the boat and we move the right pointer
right_pointer -= 1

# either way, we increment the number of boats
boats += 1

return boats
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
27 changes: 27 additions & 0 deletions algorithms/greedy/boats/test_boats_to_save_people.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import unittest
from typing import List
from parameterized import parameterized
from algorithms.greedy.boats import rescue_boats

TEST_CASES = [
([2, 1, 1], 3, 2),
([3, 1, 4, 2, 4], 4, 4),
([1, 1, 1, 1, 2], 3, 3),
([1, 2], 3, 1),
([5, 5, 5, 5], 5, 4),
([3, 2, 5, 5], 5, 3),
([1, 1, 1], 10, 2),
([5], 5, 1),
([3, 3, 3, 3], 3, 4),
]


class RescueBoatsTestCase(unittest.TestCase):
@parameterized.expand(TEST_CASES)
def test_rescue_boats(self, people: List[int], limit: int, expected: int):
actual = rescue_boats(people, limit)
self.assertEqual(expected, actual)


if __name__ == "__main__":
unittest.main()
13 changes: 13 additions & 0 deletions algorithms/greedy/spread_stones/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
# Minimum Moves to Spread Stones Over Grid

Given a 2D grid of integers of size (3 × 3), where each value represents the number of stones in the given cell, return
the minimum number of moves required to place exactly one stone in each grid cell.

## Constraints

- Only one stone can be moved in one move.
- Stone from a cell can only be moved to another cell if they are adjacent (share a side).
- The sum of all stones in the grid must be equal to 9.
- `grid.length`, `grid[i].length` == 3
- 0 <= `grid[i][j]` <= 9

44 changes: 44 additions & 0 deletions algorithms/greedy/spread_stones/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
from typing import List
from itertools import permutations


def minimum_moves(grid: List[List[int]]) -> int:
"""
This function finds the minimum number of moves required to place exactly one stone in each grid cell.

Args:
grid(list): 2D grid of integers of size (3 × 3), where each value represents the number of stones in the given
cell
Returns:
int: minimum number of moves required to place exactly one stone in each grid cell
"""
# stores every stone beyond the first one in a cell, this becomes the source
surplus_list = []
# for every cell with 0 stones, this contains the coordinates of that cell. this becomes the target
empty_list = []

minimum_number_of_moves = float("inf")

# iterate through the grid and add the cell that has surplus stones multiple times to the surplus list, for example,
# if cell grid[1][1] has 3 stones, add it twice to the surplus list
for row in range(len(grid)):
for col in range(len(grid[row])):
if grid[row][col] == 0:
empty_list.append([row, col])
elif grid[row][col] > 1:
count = grid[row][col]
for c in range(count - 1):
surplus_list.append([row, col])

# iterate through every possible permutation of the sources, trying every possible assignment to the targets
for perms in permutations(surplus_list):
# calculate the total number of moves for this permutation
total_moves = 0
for i in range(len(perms)):
total_moves += abs(perms[i][0] - empty_list[i][0]) + abs(
perms[i][1] - empty_list[i][1]
)
# return the minimum number of moves
minimum_number_of_moves = min(minimum_number_of_moves, total_moves)

return minimum_number_of_moves
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import unittest
from typing import List
from parameterized import parameterized
from algorithms.greedy.spread_stones import minimum_moves


TEST_CASES = [
([[0, 0, 1], [1, 0, 4], [1, 1, 1]], 6),
([[0, 2, 1], [1, 1, 1], [1, 1, 1]], 1),
([[0, 0, 0], [9, 0, 0], [0, 0, 0]], 15),
([[6, 0, 0], [1, 0, 0], [1, 0, 1]], 11),
([[0, 0, 0], [7, 0, 1], [0, 0, 1]], 10),
]


class MinimumMovesToSpreadStonesTestCase(unittest.TestCase):
@parameterized.expand(TEST_CASES)
def test_something(self, grid: List[List[int]], expected: int):
actual = minimum_moves(grid)
self.assertEqual(expected, actual)


if __name__ == "__main__":
unittest.main()