diff --git a/DIRECTORY.md b/DIRECTORY.md index 0436e929..1a39d050 100644 --- a/DIRECTORY.md +++ b/DIRECTORY.md @@ -519,6 +519,8 @@ * Unique Number Of Occurrences * [Test Unique Occurrences](https://github.com/BrianLusina/PythonSnips/blob/master/puzzles/hashmap/unique_number_of_occurrences/test_unique_occurrences.py) * Heap + * Maximal Score After K Operations + * [Test Maximal Score](https://github.com/BrianLusina/PythonSnips/blob/master/puzzles/heap/maximal_score_after_k_operations/test_maximal_score.py) * Maximum Subsequence Score * [Test Max Subsequence Score](https://github.com/BrianLusina/PythonSnips/blob/master/puzzles/heap/maximum_subsequence_score/test_max_subsequence_score.py) * Min Cost Hire K Workers diff --git a/puzzles/heap/maximal_score_after_k_operations/README.md b/puzzles/heap/maximal_score_after_k_operations/README.md new file mode 100644 index 00000000..75ef6068 --- /dev/null +++ b/puzzles/heap/maximal_score_after_k_operations/README.md @@ -0,0 +1,76 @@ +# Maximal Score After Applying K Operations + +You are given an array of integers nums and an integer k. You want to perform the following operation exactly k times: + +You are given a 0-indexed array of integer nums and an integer k. Your task is to maximize a score through a series of +operations. Initially, your score is set to 0 + +In each operation: + +1. Select an index i (where 0 ≤ i The ceiling function `ceil(value)` is the least integer greater than or equal to `value`. + +Constraints: + +- 1 ≤ nums.length, k ≤ 10^3 +- 1 ≤ nums[i] ≤ 10^5 + +## Examples + +Example 1: + +![Example 1](images/examples/maximal_score_after_k_operations_example_1.png) +![Example 2](images/examples/maximal_score_after_k_operations_example_2.png) +![Example 3](images/examples/maximal_score_after_k_operations_example_3.png) + +--- + +## Solution + +This algorithm maximizes the score by iteratively selecting and reducing the largest elements from the array. It uses a +max heap to ensure efficient access to the largest element. Over k iterations, the algorithm repeatedly extracts the +largest value, adds it to the total score, reduces it by dividing it by 3 and rounding it up, and reinserts the reduced +value into the heap. + +The steps of the algorithm are as follows: + +1. Create a max heap to store all elements of nums. +2. Initialize a variable score to 0 to keep track of the accumulated score. +3. Iterate for k steps, and in each iteration, perform the following steps: + - Pop the largest value from the heap using heapq.heappop and store this value in a variable largest. + - Add this value to the score. + - Calculate the reduced value of the extracted element as math.ceil(largest / 3). + - Push the reduced value back into the heap using heapq.heappush to maintain the max heap. +4. After k iterations, return the accumulated score. + +Let’s look at the following illustration to get a better understanding of the solution: + +![Solution 1](images/solution/maximal_score_after_k_operations_solution_1.png) +![Solution 2](images/solution/maximal_score_after_k_operations_solution_2.png) +![Solution 3](images/solution/maximal_score_after_k_operations_solution_3.png) +![Solution 4](images/solution/maximal_score_after_k_operations_solution_4.png) +![Solution 5](images/solution/maximal_score_after_k_operations_solution_5.png) +![Solution 6](images/solution/maximal_score_after_k_operations_solution_6.png) +![Solution 7](images/solution/maximal_score_after_k_operations_solution_7.png) +![Solution 8](images/solution/maximal_score_after_k_operations_solution_8.png) +![Solution 9](images/solution/maximal_score_after_k_operations_solution_9.png) +![Solution 10](images/solution/maximal_score_after_k_operations_solution_10.png) +![Solution 11](images/solution/maximal_score_after_k_operations_solution_11.png) +![Solution 12](images/solution/maximal_score_after_k_operations_solution_12.png) +![Solution 13](images/solution/maximal_score_after_k_operations_solution_13.png) +![Solution 14](images/solution/maximal_score_after_k_operations_solution_14.png) +![Solution 15](images/solution/maximal_score_after_k_operations_solution_15.png) + +### Time Complexity + +In each iteration of the loop, pop from the heap takes O(log(n)), while pushing the updated value into the heap also +takes O(logn). As the loop runs k times, the total time complexity for the loop is O(klogn). + +### Space Complexity + +The space complexity is O(n) because the heap stores n elements, where n is the number of elements in nums. diff --git a/puzzles/heap/maximal_score_after_k_operations/__init__.py b/puzzles/heap/maximal_score_after_k_operations/__init__.py new file mode 100644 index 00000000..3544fab9 --- /dev/null +++ b/puzzles/heap/maximal_score_after_k_operations/__init__.py @@ -0,0 +1,26 @@ +from typing import List +from math import ceil +import heapq + + +def max_score(nums: List[int], k: int) -> int: + score = 0 + + # if there are no elements, just return the score of 0 + if len(nums) == 0: + return score + + # a heapq provides a in-heap, so we'll have to use negative values to simulate a max-heap + # Create a max-heap by negating all values (heapq is a min-heap) + # This allows us to efficiently get the maximum element each time + max_heap = [-num for num in nums] + heapq.heapify(max_heap) + + for _ in range(k): + # Extract the maximum element (most negative in our negated heap) + current_largest = -heapq.heappop(max_heap) + score += current_largest + reduced_value = ceil(current_largest / 3) + heapq.heappush(max_heap, -reduced_value) + + return score diff --git a/puzzles/heap/maximal_score_after_k_operations/images/examples/maximal_score_after_k_operations_example_1.png b/puzzles/heap/maximal_score_after_k_operations/images/examples/maximal_score_after_k_operations_example_1.png new file mode 100644 index 00000000..63c120ca Binary files /dev/null and b/puzzles/heap/maximal_score_after_k_operations/images/examples/maximal_score_after_k_operations_example_1.png differ diff --git a/puzzles/heap/maximal_score_after_k_operations/images/examples/maximal_score_after_k_operations_example_2.png b/puzzles/heap/maximal_score_after_k_operations/images/examples/maximal_score_after_k_operations_example_2.png new file mode 100644 index 00000000..66a56f82 Binary files /dev/null and b/puzzles/heap/maximal_score_after_k_operations/images/examples/maximal_score_after_k_operations_example_2.png differ diff --git a/puzzles/heap/maximal_score_after_k_operations/images/examples/maximal_score_after_k_operations_example_3.png b/puzzles/heap/maximal_score_after_k_operations/images/examples/maximal_score_after_k_operations_example_3.png new file mode 100644 index 00000000..7564a0df Binary files /dev/null and b/puzzles/heap/maximal_score_after_k_operations/images/examples/maximal_score_after_k_operations_example_3.png differ diff --git a/puzzles/heap/maximal_score_after_k_operations/images/solution/maximal_score_after_k_operations_solution_1.png b/puzzles/heap/maximal_score_after_k_operations/images/solution/maximal_score_after_k_operations_solution_1.png new file mode 100644 index 00000000..db244086 Binary files /dev/null and b/puzzles/heap/maximal_score_after_k_operations/images/solution/maximal_score_after_k_operations_solution_1.png differ diff --git a/puzzles/heap/maximal_score_after_k_operations/images/solution/maximal_score_after_k_operations_solution_10.png b/puzzles/heap/maximal_score_after_k_operations/images/solution/maximal_score_after_k_operations_solution_10.png new file mode 100644 index 00000000..ed3d2985 Binary files /dev/null and b/puzzles/heap/maximal_score_after_k_operations/images/solution/maximal_score_after_k_operations_solution_10.png differ diff --git a/puzzles/heap/maximal_score_after_k_operations/images/solution/maximal_score_after_k_operations_solution_11.png b/puzzles/heap/maximal_score_after_k_operations/images/solution/maximal_score_after_k_operations_solution_11.png new file mode 100644 index 00000000..acbac7e0 Binary files /dev/null and b/puzzles/heap/maximal_score_after_k_operations/images/solution/maximal_score_after_k_operations_solution_11.png differ diff --git a/puzzles/heap/maximal_score_after_k_operations/images/solution/maximal_score_after_k_operations_solution_12.png b/puzzles/heap/maximal_score_after_k_operations/images/solution/maximal_score_after_k_operations_solution_12.png new file mode 100644 index 00000000..17f874c7 Binary files /dev/null and b/puzzles/heap/maximal_score_after_k_operations/images/solution/maximal_score_after_k_operations_solution_12.png differ diff --git a/puzzles/heap/maximal_score_after_k_operations/images/solution/maximal_score_after_k_operations_solution_13.png b/puzzles/heap/maximal_score_after_k_operations/images/solution/maximal_score_after_k_operations_solution_13.png new file mode 100644 index 00000000..b0cd2d73 Binary files /dev/null and b/puzzles/heap/maximal_score_after_k_operations/images/solution/maximal_score_after_k_operations_solution_13.png differ diff --git a/puzzles/heap/maximal_score_after_k_operations/images/solution/maximal_score_after_k_operations_solution_14.png b/puzzles/heap/maximal_score_after_k_operations/images/solution/maximal_score_after_k_operations_solution_14.png new file mode 100644 index 00000000..393e7f72 Binary files /dev/null and b/puzzles/heap/maximal_score_after_k_operations/images/solution/maximal_score_after_k_operations_solution_14.png differ diff --git a/puzzles/heap/maximal_score_after_k_operations/images/solution/maximal_score_after_k_operations_solution_15.png b/puzzles/heap/maximal_score_after_k_operations/images/solution/maximal_score_after_k_operations_solution_15.png new file mode 100644 index 00000000..cbdc2810 Binary files /dev/null and b/puzzles/heap/maximal_score_after_k_operations/images/solution/maximal_score_after_k_operations_solution_15.png differ diff --git a/puzzles/heap/maximal_score_after_k_operations/images/solution/maximal_score_after_k_operations_solution_2.png b/puzzles/heap/maximal_score_after_k_operations/images/solution/maximal_score_after_k_operations_solution_2.png new file mode 100644 index 00000000..47b95dd3 Binary files /dev/null and b/puzzles/heap/maximal_score_after_k_operations/images/solution/maximal_score_after_k_operations_solution_2.png differ diff --git a/puzzles/heap/maximal_score_after_k_operations/images/solution/maximal_score_after_k_operations_solution_3.png b/puzzles/heap/maximal_score_after_k_operations/images/solution/maximal_score_after_k_operations_solution_3.png new file mode 100644 index 00000000..cf327a98 Binary files /dev/null and b/puzzles/heap/maximal_score_after_k_operations/images/solution/maximal_score_after_k_operations_solution_3.png differ diff --git a/puzzles/heap/maximal_score_after_k_operations/images/solution/maximal_score_after_k_operations_solution_4.png b/puzzles/heap/maximal_score_after_k_operations/images/solution/maximal_score_after_k_operations_solution_4.png new file mode 100644 index 00000000..913e8a62 Binary files /dev/null and b/puzzles/heap/maximal_score_after_k_operations/images/solution/maximal_score_after_k_operations_solution_4.png differ diff --git a/puzzles/heap/maximal_score_after_k_operations/images/solution/maximal_score_after_k_operations_solution_5.png b/puzzles/heap/maximal_score_after_k_operations/images/solution/maximal_score_after_k_operations_solution_5.png new file mode 100644 index 00000000..36a4c8d9 Binary files /dev/null and b/puzzles/heap/maximal_score_after_k_operations/images/solution/maximal_score_after_k_operations_solution_5.png differ diff --git a/puzzles/heap/maximal_score_after_k_operations/images/solution/maximal_score_after_k_operations_solution_6.png b/puzzles/heap/maximal_score_after_k_operations/images/solution/maximal_score_after_k_operations_solution_6.png new file mode 100644 index 00000000..0afdcd70 Binary files /dev/null and b/puzzles/heap/maximal_score_after_k_operations/images/solution/maximal_score_after_k_operations_solution_6.png differ diff --git a/puzzles/heap/maximal_score_after_k_operations/images/solution/maximal_score_after_k_operations_solution_7.png b/puzzles/heap/maximal_score_after_k_operations/images/solution/maximal_score_after_k_operations_solution_7.png new file mode 100644 index 00000000..8530f89c Binary files /dev/null and b/puzzles/heap/maximal_score_after_k_operations/images/solution/maximal_score_after_k_operations_solution_7.png differ diff --git a/puzzles/heap/maximal_score_after_k_operations/images/solution/maximal_score_after_k_operations_solution_8.png b/puzzles/heap/maximal_score_after_k_operations/images/solution/maximal_score_after_k_operations_solution_8.png new file mode 100644 index 00000000..461e5271 Binary files /dev/null and b/puzzles/heap/maximal_score_after_k_operations/images/solution/maximal_score_after_k_operations_solution_8.png differ diff --git a/puzzles/heap/maximal_score_after_k_operations/images/solution/maximal_score_after_k_operations_solution_9.png b/puzzles/heap/maximal_score_after_k_operations/images/solution/maximal_score_after_k_operations_solution_9.png new file mode 100644 index 00000000..97d6f959 Binary files /dev/null and b/puzzles/heap/maximal_score_after_k_operations/images/solution/maximal_score_after_k_operations_solution_9.png differ diff --git a/puzzles/heap/maximal_score_after_k_operations/test_maximal_score.py b/puzzles/heap/maximal_score_after_k_operations/test_maximal_score.py new file mode 100644 index 00000000..8890f4c4 --- /dev/null +++ b/puzzles/heap/maximal_score_after_k_operations/test_maximal_score.py @@ -0,0 +1,77 @@ +import unittest +from . import max_score + + +class MaximalScoreAfterKOperationsTestCase(unittest.TestCase): + def test_1(self): + nums = [10,20,30,40,50] + k = 4 + expected = 140 + actual = max_score(nums, k) + self.assertEqual(expected, actual) + + def test_2(self): + nums = [5,12,7,3,10] + k = 3 + expected = 29 + actual = max_score(nums, k) + self.assertEqual(expected, actual) + + def test_3(self): + nums = [6,9,15] + k = 2 + expected = 24 + actual = max_score(nums, k) + self.assertEqual(expected, actual) + + def test_4(self): + nums = [1,10,3,3,3] + k = 3 + expected = 17 + actual = max_score(nums, k) + self.assertEqual(expected, actual) + + def test_5(self): + nums = [7,10,16] + k = 2 + expected = 26 + actual = max_score(nums, k) + self.assertEqual(expected, actual) + + def test_6(self): + nums = [5,120,7,30,10] + k = 3 + expected = 190 + actual = max_score(nums, k) + self.assertEqual(expected, actual) + + def test_7(self): + nums = [100,200,300,400,500] + k = 4 + expected = 1400 + actual = max_score(nums, k) + self.assertEqual(expected, actual) + + def test_8(self): + nums = [20,20,20,20] + k = 3 + expected = 60 + actual = max_score(nums, k) + self.assertEqual(expected, actual) + + def test_9(self): + nums = [81698,68947,77662,46592,13226,37325,2800,22504,99833,77083,38068,40934,3640,33631,84634,66457,21309, + 64949,94392,3553,68692,31662,17348,42805,32143,7099,88341,65391,8164,65035,22205,88755,80232,84970, + 19213,36774,33975,47386,74761,4893,9040,8263,60379,88511,49040,89068,72601,17683,17871,46156,2805,10247, + 54658,27427,51671,81935,59171,70215,56400,83874,9230,31194,98266,84404,1200,89589,70329,39209,19461, + 19022,86927,26496,27561,96403,78150,47498,5696,78065,75672,44842,64855,19760,57351,7788,41209,89214, + 24315,6398,60738,88636,71885,44987,28782,13700,78965,47534,82496,66162,89596,3646,73107,13112,28574, + 37445,14997,98860] + k = 1000 + expected = 7709375 + actual = max_score(nums, k) + self.assertEqual(expected, actual) + + +if __name__ == '__main__': + unittest.main()