Divide Chocolate
Hard · Binary Search · Explanation and code example
Binary Search + Greedy
How it works
We want to maximize the minimum sweetness we receive after dividing the chocolate bar into k + 1 pieces. This is a classic binary search on the answer pattern. We binary search on possible sweetness values: for each candidate value mid, we greedily check if we can divide the bar so that at least k + 1 people each get a piece with sweetness at least mid. If we can, we try a higher value; if not, we try a lower one.
Steps
- Set the search range:
left = min(sweetness)andright = sum(sweetness) / (k + 1). - While
left < right:- Compute
mid = (left + right + 1) / 2. - Greedily assign consecutive chunks to people, cutting whenever the accumulated sweetness reaches
mid. - Count how many people receive a piece with sweetness at least
mid.
- Compute
- If at least
k + 1people can receive such a piece, setleft = mid. - Otherwise, set
right = mid - 1. - Return
rightas the maximum possible minimum sweetness.
Complexity
-
Time complexity:
O(n × log(S/(k + 1)))- The lower and upper bounds are
min(sweetness)andS / (k + 1)respectively. In the worst case (whenkis small), the right boundary will have the same magnitude asS, and the left boundary will be1. Thus, the maximum possible time complexity for a single binary search isO(log S). For every single search, we need to traverse the chocolate bar in order to allocate chocolate chunks to everyone, which takesO(n)time.
- The lower and upper bounds are
-
Space complexity:
O(1)constant space
Where
nis the number of chunks in the chocolate, andSis the total sweetness of the chocolate bar.
Things to watch for
Off-by-One in Number of Pieces
The problem asks to divide chocolate among k + 1 people (yourself plus k friends). A common mistake is dividing into exactly k pieces instead of k + 1.
Using Wrong Binary Search Variant
This problem requires finding the maximum minimum value, which needs the "upper-bound" binary search variant. Using the standard lower-bound variant leads to incorrect results or infinite loops.
Incorrect Greedy Cutting Logic
When checking if a target sweetness is achievable, a common mistake is cutting as soon as accumulated sweetness equals the target, rather than when it reaches or exceeds the target. This fails when exact division is not possible.
Forgetting to Reset Accumulator After Cutting
After making a cut and counting a valid piece, forgetting to reset the sweetness accumulator causes incorrect counting of subsequent pieces.
Taking the First Valid Piece Instead of the Minimum
Since you get the piece with minimum sweetness and friends take theirs first, the greedy approach naturally gives you the last remaining piece. Some mistakenly try to track the minimum explicitly, which complicates the solution unnecessarily.
Python code example
from typing import List
class Solution:
def maximizeSweetness(self, sweetness: List[int], k: int) -> int:
# Initialize the left and right boundaries.
# left = 1 and right = (total sweetness) / (number of people).
number_of_people = k + 1
left = min(sweetness)
right = sum(sweetness) // number_of_people
while left < right:
# Get the middle index between left and right boundary indexes.
# cur_sweetness stands for the total sweetness for the current person.
# people_with_chocolate stands for the number of people that have
# a piece of chocolate of sweetness greater than or equal to mid.
mid = (left + right + 1) // 2
cur_sweetness = 0
people_with_chocolate = 0
# Start assigning chunks to the current person.
for s in sweetness:
cur_sweetness += s
# If the total sweetness is no less than mid, this means we can break off
# the current piece and move on to assigning chunks to the next person.
if cur_sweetness >= mid:
people_with_chocolate += 1
cur_sweetness = 0
if people_with_chocolate >= k + 1:
left = mid
else:
right = mid - 1
return right
C++ code example
class Solution {
public:
int maximizeSweetness(vector<int>& sweetness, int k) {
// Initialize the left and right boundaries.
// left = 1 and right = total sweetness / number of people.
int numberOfPeople = k + 1;
int left = *min_element(sweetness.begin(), sweetness.end());
int right = accumulate(sweetness.begin(), sweetness.end(), 0) / numberOfPeople;
while (left < right) {
// Get the middle index between left and right boundary indexes.
// cur_sweetness stands for the total sweetness for the current person.
// people_with_chocolate stands for the number of people that have
// a piece of chocolate of sweetness greater than or equal to mid.
int mid = (left + right + 1) / 2;
int curSweetness = 0;
int peopleWithChocolate = 0;
// Start assigning chunks to the current people,.
for (int s : sweetness) {
curSweetness += s;
// When the total sweetness from him is no less than mid, meaning we
// have done with him and move on to assigning chunks to the next people.
if (curSweetness >= mid) {
peopleWithChocolate += 1;
curSweetness = 0;
}
}
// Check if we successfully give everyone a piece of chocolate with sweetness
// no less than mid, and eliminate the search space by half.
if (peopleWithChocolate >= numberOfPeople) {
left = mid;
} else {
right = mid - 1;
}
}
// Once the left and right boundaries coincide, we find the target value,
// that is, the maximum possible sweetness we can get.
return right;
}
};
Source and license
Adapted from NeetCode’s source · MIT license. Node types are supplied by LeetCode where needed.