Skip to content

Commit ce7438e

Browse files
authored
Merge pull request #126 from BrianLusina/feat/divide-chocolate
feat(algorithms, binary-search): divide chocolate
2 parents 332e060 + f918257 commit ce7438e

File tree

5 files changed

+168
-1
lines changed

5 files changed

+168
-1
lines changed

DIRECTORY.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,8 @@
120120
* [Petethebaker](https://github.com/BrianLusina/PythonSnips/blob/master/algorithms/petethebaker.py)
121121
* Search
122122
* Binary Search
123+
* Divide Chocolate
124+
* [Test Divide Chocolate](https://github.com/BrianLusina/PythonSnips/blob/master/algorithms/search/binary_search/divide_chocolate/test_divide_chocolate.py)
123125
* Maxruntime N Computers
124126
* [Test Max Runtime](https://github.com/BrianLusina/PythonSnips/blob/master/algorithms/search/binary_search/maxruntime_n_computers/test_max_runtime.py)
125127
* [Test Binary Search](https://github.com/BrianLusina/PythonSnips/blob/master/algorithms/search/binary_search/test_binary_search.py)

algorithms/intervals/remove_intervals/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ def remove_covered_intervals(intervals: List[List[int]]) -> int:
3030
intervals.sort(key=lambda x: (x[0], -x[1]))
3131

3232
# keep track of the last max end seen so far, we use a large negative infinity to cover all possible numbers
33-
max_end_seen = float('-inf')
33+
max_end_seen = float("-inf")
3434
count = 0
3535

3636
# We then iterate through the given intervals
Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
# Divide Chocolate
2+
3+
You have a chocolate bar made up of several chunks, and each chunk has a certain sweetness, given in an array called
4+
sweetness. You want to share the chocolate with k friends. To do this, you’ll make k cuts to divide the bar into k + 1
5+
parts. Each part will consist of consecutive chunks.
6+
7+
Being a kind person, you’ll take the piece with the minimum total sweetness and give the other pieces to your friends.
8+
9+
Your task is to find the maximum total sweetness of the piece you will receive if you cut the chocolate bar optimally.
10+
11+
## Constraints
12+
13+
- 0 <= k < sweetness.length <= 10^3
14+
- 1 <= sweetness[i] <= 10^3
15+
16+
## Solution
17+
18+
As we have a chocolate bar made of several chunks, each with its own sweetness value, dividing the chocolate evenly
19+
won’t ensure that we get the chocolate part with the maximum possible minimum sweetness. The next natural thought for
20+
solving this problem is to try to cut the chocolate in different ways and find out the one that gives the desired result.
21+
But it becomes very inefficient when the chocolate bar is long. There are just too many possible combinations of where
22+
to cut, and checking each one would take too much time.
23+
24+
So, instead of guessing exactly where to cut, we flip the problem and ask: What is the largest minimum sweetness we can
25+
guarantee for ourselves if we divide the chocolate bar into k + 1 pieces?
26+
27+
The idea is to guess a sweetness value, called S , and check if it’s possible to cut the chocolate into k + 1 parts,
28+
where each part has at least S sweetness. Go through the chocolate chunks one by one, adding their sweetness. When the
29+
total reaches S or more, count it as one piece and start a new one. If it’s possible to make at least k + 1 such pieces,
30+
then S is a good guess. If not, the guess is too high and should be lowered. For example, if S=5 , keep adding chunks
31+
until the total is 5 or more. Count that as one piece and reset the total to 0 . Repeat this through the whole chocolate
32+
bar.
33+
34+
The sweetness value is guessed using the binary search. The lower bound is 1, and the upper bound is the total sweetness
35+
divided by (k + 1), as no single piece can have more than that if the chocolate is divided evenly. Start by guessing the
36+
middle value. If it allows at least k + 1 valid parts, try a higher value. Otherwise, try a lower one. This quickly
37+
narrows down the largest sweetness that can be guaranteed.
38+
39+
Now, let’s look at the algorithm steps below:
40+
41+
1. Initialize the binary search range:
42+
- Set low (the smallest possible minimum sweetness for a piece) as 1, because sweetness values are at least 1.
43+
- Set high (the largest possible minimum sweetness) as the total sweetness divided evenly among k + 1 people, using
44+
the formula sum(sweetness) // (k + 1). This is the best possible sweetness each person could get if the chocolate
45+
were divided perfectly.
46+
47+
2. Start the binary search, and while low is less than or equal to high, do the following:
48+
- Calculate the mid-point value, mid = (low + high) // 2. This represents the current guess for the maximum minimum
49+
sweetness we might be able to give to each person.
50+
- Use the helper function canDivide(sweetness, k, mid) to check if dividing the chocolate into at least k + 1 pieces
51+
is possible, where each piece has a total sweetness of at least mid. The helper works by summing chunks and counting
52+
how many full pieces it can form with at least mid sweetness.
53+
- If it’s possible to divide into k + 1 or more such pieces:
54+
- That means we can afford to aim for even higher sweetness per person. So we move the lower bound up: low = mid + 1.
55+
- We also store mid as a potential result, result.
56+
57+
3. If it’s not possible to make enough pieces:
58+
- Then our guess mid was too high, so we reduce the upper bound: high = mid - 1.
59+
60+
4. After the binary search ends, return the last valid result, which is the maximum possible minimum sweetness we can
61+
guarantee for each person.
62+
63+
### Time Complexity
64+
65+
Let n be the total number of chunks in the chocolate bar, and S be the upper bound for our binary‐search range, i.e.,
66+
the sum of values in the sweetness list divided by k + 1. Each call to the helper function scans the entire sweetness
67+
array once, taking O(n) time. The binary search is performed over the integer range [1…S], which takes O(log S) iterations.
68+
Therefore, the overall time complexity of this solution is O(n×logS).
69+
70+
### Space Complexity
71+
72+
We only use a fixed number of extra variables, and no additional arrays or data structures. Therefore the space complexity
73+
is O(1).
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
from typing import List
2+
3+
4+
def maximize_sweetness(sweetness: List[int], k: int) -> int:
5+
"""
6+
Finds the maximum possible sweetness of the piece you will receive if you cut the chocolate bar optimally.
7+
Args:
8+
sweetness (List[int]): A list of sweetness values for each chunk of the chocolate bar.
9+
k (int): The number of friends to share the chocolate bar with.
10+
Returns:
11+
int: The maximum possible sweetness of the piece you will receive if you cut the chocolate bar optimally.
12+
"""
13+
lower_bound = 1
14+
upper_bound = sum(sweetness) // (k + 1)
15+
16+
while lower_bound < upper_bound:
17+
current_sweetness = 0
18+
pieces_count = 0
19+
mid = (lower_bound + upper_bound + 1) // 2
20+
21+
for s in sweetness:
22+
current_sweetness += s
23+
if current_sweetness >= mid:
24+
pieces_count += 1
25+
current_sweetness = 0
26+
27+
# check if pieces_count is at least k+1
28+
if pieces_count >= k + 1:
29+
lower_bound = mid
30+
else:
31+
upper_bound = mid - 1
32+
33+
return lower_bound
34+
35+
36+
def maximize_sweetness_2(sweetness: List[int], k: int) -> int:
37+
"""
38+
Finds the maximum possible sweetness of the piece you will receive if you cut the chocolate bar optimally.
39+
Args:
40+
sweetness (List[int]): A list of sweetness values for each chunk of the chocolate bar.
41+
k (int): The number of friends to share the chocolate bar with.
42+
Returns:
43+
int: The maximum possible sweetness of the piece you will receive if you cut the chocolate bar optimally.
44+
"""
45+
low, high = 1, sum(sweetness) // (k + 1)
46+
result = low
47+
while low <= high:
48+
mid = (low + high) // 2
49+
if can_divide(sweetness, k, mid):
50+
result = mid
51+
low = mid + 1
52+
else:
53+
high = mid - 1
54+
return result
55+
56+
57+
def can_divide(sweetness: List[int], k: int, min_sweetness: int) -> bool:
58+
total_sweetness, pieces = 0, 0
59+
for sweet in sweetness:
60+
total_sweetness += sweet
61+
if total_sweetness >= min_sweetness:
62+
pieces += 1
63+
total_sweetness = 0
64+
return pieces >= k + 1
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
import unittest
2+
from typing import List
3+
from parameterized import parameterized
4+
from algorithms.search.binary_search.divide_chocolate import maximize_sweetness, maximize_sweetness_2
5+
6+
TEST_CASES = [
7+
([1, 2, 3, 4, 5, 6, 7, 8, 9], 5, 6),
8+
([5], 0, 5),
9+
([1, 2, 2, 1, 2, 2, 1], 3, 2),
10+
([1, 1, 1, 1, 1, 1, 1], 6, 1),
11+
([7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7], 20, 7),
12+
]
13+
14+
15+
class MaximizeSweetnessTestCase(unittest.TestCase):
16+
@parameterized.expand(TEST_CASES)
17+
def test_maximize_sweetness(self, sweetness: List[int], k: int, expected: int):
18+
actual = maximize_sweetness(sweetness, k)
19+
self.assertEqual(expected, actual)
20+
21+
@parameterized.expand(TEST_CASES)
22+
def test_maximize_sweetness_2(self, sweetness: List[int], k: int, expected: int):
23+
actual = maximize_sweetness_2(sweetness, k)
24+
self.assertEqual(expected, actual)
25+
26+
27+
if __name__ == "__main__":
28+
unittest.main()

0 commit comments

Comments
 (0)