2234. Maximum Total Beauty of the Gardens
Problem Description
Alice manages n
gardens and wants to maximize their total beauty by planting additional flowers.
You're given:
- An array
flowers
whereflowers[i]
represents the number of flowers already planted in gardeni
(these cannot be removed) newFlowers
: the maximum number of additional flowers Alice can planttarget
: the minimum number of flowers needed for a garden to be considered "complete"full
: the beauty value contributed by each complete gardenpartial
: the beauty value multiplier for the minimum flower count among incomplete gardens
A garden is complete if it has at least target
flowers. The total beauty is calculated as:
- Number of complete gardens ×
full
- Plus: Minimum flower count among all incomplete gardens ×
partial
(or 0 if all gardens are complete)
The goal is to distribute up to newFlowers
additional flowers among the gardens to maximize the total beauty.
For example, if you have 3 gardens with [1, 2, 4]
flowers, target = 4
, full = 5
, partial = 2
, and newFlowers = 3
:
- Garden 2 already has 4 flowers (complete)
- You could add 2 flowers to garden 1 to make it complete (having 4 flowers)
- This would give: 2 complete gardens × 5 + minimum of remaining incomplete gardens (2) × 2 = 10 + 4 = 14 total beauty
- Alternatively, you could distribute flowers to increase the minimum among incomplete gardens for a different beauty value
The challenge is finding the optimal balance between creating more complete gardens versus increasing the minimum flower count in incomplete gardens to maximize the total beauty score.
Intuition
The key insight is recognizing that we face a trade-off: we can either use our additional flowers to create more complete gardens (earning full
points each) or increase the minimum flower count among incomplete gardens (earning partial
points per flower in the minimum garden).
First, we should sort the gardens by their current flower count. This helps us identify which gardens are easiest to complete (those closest to target
) and which incomplete gardens we can efficiently boost together.
Why enumerate the number of complete gardens? Since we want to maximize beauty, we need to try different configurations. If we decide to have x
complete gardens, we should complete the x
gardens that already have the most flowers - this minimizes the cost and leaves more flowers for boosting incomplete gardens.
For each configuration with x
complete gardens:
- We complete the rightmost
x
gardens in our sorted array (they need the fewest additional flowers) - With the remaining flowers, we want to maximize the minimum among the incomplete gardens
The second part is clever: to maximize the minimum efficiently, we should bring all gardens up to the same level together. If we have gardens with [1, 2, 3]
flowers and want to increase the minimum, we should first bring them all to 3, then all to 4, and so on. This is why we use binary search - to find the highest level we can afford to bring a group of gardens to.
The binary search finds the largest prefix of incomplete gardens we can afford to level up to the same height. The cost to bring gardens 0
to mid
up to height flowers[mid]
is flowers[mid] * (mid + 1) - sum(flowers[0:mid+1])
. If we have extra flowers after this leveling, we can increase all these gardens further by dividing the remaining flowers equally among them.
By trying all possible values of x
(number of complete gardens) and finding the optimal minimum for incomplete gardens in each case, we guarantee finding the maximum total beauty.
Learn more about Greedy, Two Pointers, Binary Search, Prefix Sum and Sorting patterns.
Solution Approach
First, we sort the flowers
array to group gardens by their current flower counts. We also create a prefix sum array s
to efficiently calculate range sums later.
flowers.sort()
n = len(flowers)
s = list(accumulate(flowers, initial=0))
We find the initial number of complete gardens using binary search:
i = n - bisect_left(flowers, target)
This tells us how many gardens already have at least target
flowers.
Now we enumerate the number of complete gardens from i
to n
:
For each choice of x
complete gardens:
-
Calculate cost to complete gardens: The garden at position
n-x
needsmax(target - flowers[n-x], 0)
additional flowers. We subtract this fromnewFlowers
. -
If we run out of flowers, we break since we can't achieve more complete gardens.
-
Binary search for optimal incomplete garden configuration: Among the remaining
n-x
incomplete gardens, we find the maximum indexl
where we can afford to bring all gardens[0...l]
to the same level asflowers[l]
.The binary search checks: can we afford to bring gardens
[0...mid]
to levelflowers[mid]
?- Cost =
flowers[mid] * (mid + 1) - s[mid + 1]
- This represents: (target level × number of gardens) - (current total flowers in those gardens)
- Cost =
-
Calculate the minimum flower count
y
:- If
l = -1
, all incomplete gardens would need more flowers than available, soy = 0
- Otherwise, after leveling gardens
[0...l]
toflowers[l]
, we havenewFlowers - cost
flowers left - We can increase all
l+1
gardens by(newFlowers - cost) // (l + 1)
flowers each - The final minimum is
min(flowers[l] + increase, target - 1)
(capped attarget-1
to keep them incomplete)
- If
-
Calculate beauty:
x * full + y * partial
The algorithm returns the maximum beauty found across all configurations.
The time complexity is O(n² log n)
where the outer loop runs O(n)
times and each iteration performs a binary search O(log n)
with O(n)
validation in the worst case. The space complexity is O(n)
for the prefix sum array.
Ready to land your dream job?
Unlock your dream job with a 5-minute evaluator for a personalized learning plan!
Start EvaluatorExample Walkthrough
Let's walk through a concrete example to illustrate the solution approach.
Input:
flowers = [2, 4, 5, 3]
newFlowers = 10
target = 6
full = 20
partial = 5
Step 1: Sort and Setup
After sorting: flowers = [2, 3, 4, 5]
Prefix sum array: s = [0, 2, 5, 9, 14]
Step 2: Find Initial Complete Gardens
Using binary search, we find that garden at index 3 (with 5 flowers) is the only one close to being complete. So initially, i = 1
(we can try having 1 to 4 complete gardens).
Step 3: Enumerate Complete Garden Configurations
Configuration 1: x = 1 complete garden (completing the rightmost garden)
- Garden at index 3 needs
6 - 5 = 1
flower to complete - Remaining flowers:
10 - 1 = 9
- Incomplete gardens:
[2, 3, 4]
at indices 0, 1, 2 - Binary search to maximize minimum:
- Can we bring all 3 gardens to level 4? Cost =
4 × 3 - (2+3+4) = 12 - 9 = 3
✓ - After bringing all to level 4, we have
9 - 3 = 6
flowers left - We can add
6 // 3 = 2
flowers to each, making them all have 6 flowers - But wait! 6 would make them complete, so cap at
target - 1 = 5
- Can we bring all 3 gardens to level 4? Cost =
- Minimum among incomplete: 5
- Beauty:
1 × 20 + 5 × 5 = 45
Configuration 2: x = 2 complete gardens
- Gardens at indices 2 and 3 need
(6-4) + (6-5) = 3
flowers total - Remaining flowers:
10 - 3 = 7
- Incomplete gardens:
[2, 3]
at indices 0, 1 - Binary search to maximize minimum:
- Can we bring both to level 3? Cost =
3 × 2 - (2+3) = 6 - 5 = 1
✓ - After bringing both to level 3, we have
7 - 1 = 6
flowers left - We can add
6 // 2 = 3
flowers to each, making them both have 6 flowers - Cap at
target - 1 = 5
- Can we bring both to level 3? Cost =
- Minimum among incomplete: 5
- Beauty:
2 × 20 + 5 × 5 = 65
Configuration 3: x = 3 complete gardens
- Gardens at indices 1, 2, 3 need
(6-3) + (6-4) + (6-5) = 6
flowers total - Remaining flowers:
10 - 6 = 4
- Incomplete gardens:
[2]
at index 0 - We can add all 4 flowers to this garden:
2 + 4 = 6
- But 6 would make it complete, so cap at 5
- Minimum among incomplete: 5
- Beauty:
3 × 20 + 5 × 5 = 85
Configuration 4: x = 4 complete gardens
- All gardens need to be complete
- Cost:
(6-2) + (6-3) + (6-4) + (6-5) = 4 + 3 + 2 + 1 = 10
flowers - Remaining flowers:
10 - 10 = 0
✓ - No incomplete gardens, so partial beauty = 0
- Beauty:
4 × 20 + 0 × 5 = 80
Result: The maximum beauty is 85, achieved by completing 3 gardens and keeping 1 incomplete garden at 5 flowers.
Solution Implementation
1class Solution:
2 def maximumBeauty(
3 self, flowers: List[int], newFlowers: int, target: int, full: int, partial: int
4 ) -> int:
5 # Sort gardens by their current flower count
6 flowers.sort()
7 n = len(flowers)
8
9 # Create prefix sum array for quick range sum queries
10 prefix_sum = list(accumulate(flowers, initial=0))
11
12 # Initialize result and count gardens already at or above target
13 max_beauty = 0
14 gardens_already_full = n - bisect_left(flowers, target)
15
16 # Try different numbers of complete gardens (from already full to all gardens)
17 for num_complete in range(gardens_already_full, n + 1):
18 # Deduct flowers needed to make the last garden complete
19 if num_complete > 0:
20 flowers_needed = max(target - flowers[n - num_complete], 0)
21 newFlowers -= flowers_needed
22
23 # If not enough flowers, stop trying more complete gardens
24 if newFlowers < 0:
25 break
26
27 # Binary search to find the maximum partial level we can achieve
28 # for the remaining incomplete gardens
29 left, right = 0, n - num_complete - 1
30 while left < right:
31 mid = (left + right + 1) >> 1
32 # Calculate cost to raise all gardens [0, mid] to flowers[mid] level
33 cost_to_level = flowers[mid] * (mid + 1) - prefix_sum[mid + 1]
34 if cost_to_level <= newFlowers:
35 left = mid
36 else:
37 right = mid - 1
38
39 # Calculate the maximum partial level achievable
40 partial_level = 0
41 if right != -1:
42 # Cost to bring all gardens [0, left] to flowers[left] level
43 base_cost = flowers[left] * (left + 1) - prefix_sum[left + 1]
44 # Distribute remaining flowers evenly, but not exceeding target-1
45 remaining_flowers = newFlowers - base_cost
46 gardens_to_upgrade = left + 1
47 partial_level = min(flowers[left] + remaining_flowers // gardens_to_upgrade, target - 1)
48
49 # Calculate beauty score and update maximum
50 beauty_score = num_complete * full + partial_level * partial
51 max_beauty = max(max_beauty, beauty_score)
52
53 return max_beauty
54
1class Solution {
2 public long maximumBeauty(int[] flowers, long newFlowers, int target, int full, int partial) {
3 // Sort the gardens by their current flower count
4 Arrays.sort(flowers);
5 int n = flowers.length;
6
7 // Build prefix sum array for efficient range sum queries
8 long[] prefixSum = new long[n + 1];
9 for (int i = 0; i < n; ++i) {
10 prefixSum[i + 1] = prefixSum[i] + flowers[i];
11 }
12
13 long maxBeauty = 0;
14
15 // Count gardens that are already complete (have >= target flowers)
16 int completeGardens = 0;
17 for (int flowerCount : flowers) {
18 if (flowerCount >= target) {
19 ++completeGardens;
20 }
21 }
22
23 // Try different numbers of complete gardens (from current count to all gardens)
24 for (; completeGardens <= n; ++completeGardens) {
25 // Calculate flowers needed to make the rightmost incomplete gardens complete
26 long flowersNeeded = (completeGardens == 0 ? 0 : Math.max(target - flowers[n - completeGardens], 0));
27 newFlowers -= flowersNeeded;
28
29 // If we don't have enough flowers, stop trying
30 if (newFlowers < 0) {
31 break;
32 }
33
34 // Binary search to find the maximum garden index we can raise to the same level
35 // with remaining flowers (among incomplete gardens)
36 int left = 0, right = n - completeGardens - 1;
37 while (left < right) {
38 int mid = (left + right + 1) >> 1;
39 // Calculate cost to raise gardens [0, mid] to flowers[mid] level
40 long costToLevel = (long) flowers[mid] * (mid + 1) - prefixSum[mid + 1];
41 if (costToLevel <= newFlowers) {
42 left = mid;
43 } else {
44 right = mid - 1;
45 }
46 }
47
48 // Calculate the minimum flower count among incomplete gardens
49 long minIncompleteFlowers = 0;
50 if (right != -1) {
51 // Cost to raise gardens [0, left] to flowers[left] level
52 long costToLevel = (long) flowers[left] * (left + 1) - prefixSum[left + 1];
53 // Calculate the achievable minimum (capped at target - 1 to keep them incomplete)
54 minIncompleteFlowers = Math.min(flowers[left] + (newFlowers - costToLevel) / (left + 1), target - 1);
55 }
56
57 // Calculate beauty score: complete gardens * full + minimum incomplete * partial
58 long currentBeauty = (long) completeGardens * full + minIncompleteFlowers * partial;
59 maxBeauty = Math.max(maxBeauty, currentBeauty);
60
61 // Restore newFlowers for next iteration
62 newFlowers += flowersNeeded;
63 }
64
65 return maxBeauty;
66 }
67}
68
1class Solution {
2public:
3 long long maximumBeauty(vector<int>& flowers, long long newFlowers, int target, int full, int partial) {
4 // Sort flowers in ascending order to process from smallest to largest
5 sort(flowers.begin(), flowers.end());
6 int n = flowers.size();
7
8 // Build prefix sum array for quick range sum queries
9 // prefixSum[i] = sum of first i elements
10 long long prefixSum[n + 1];
11 prefixSum[0] = 0;
12 for (int i = 1; i <= n; ++i) {
13 prefixSum[i] = prefixSum[i - 1] + flowers[i - 1];
14 }
15
16 long long maxBeauty = 0;
17
18 // Count how many gardens are already at or above target
19 int alreadyFull = flowers.end() - lower_bound(flowers.begin(), flowers.end(), target);
20
21 // Try different numbers of full gardens (from already full to all gardens)
22 for (int numFullGardens = alreadyFull; numFullGardens <= n; ++numFullGardens) {
23 // Calculate flowers needed to make the last garden full (if not already)
24 if (numFullGardens > 0) {
25 int gardenIndex = n - numFullGardens;
26 newFlowers -= max(target - flowers[gardenIndex], 0);
27 }
28
29 // If we don't have enough flowers, we can't make more gardens full
30 if (newFlowers < 0) {
31 break;
32 }
33
34 // Binary search to find the maximum number of incomplete gardens we can raise
35 // to the same height with remaining flowers
36 int left = 0, right = n - numFullGardens - 1;
37 while (left < right) {
38 int mid = (left + right + 1) >> 1;
39 // Calculate cost to raise first (mid+1) gardens to height of flowers[mid]
40 long long costToLevel = 1LL * flowers[mid] * (mid + 1) - prefixSum[mid + 1];
41 if (costToLevel <= newFlowers) {
42 left = mid;
43 } else {
44 right = mid - 1;
45 }
46 }
47
48 // Calculate the maximum height we can achieve for incomplete gardens
49 int maxIncompleteHeight = 0;
50 if (right != -1) {
51 // Cost to raise first (left+1) gardens to height of flowers[left]
52 long long costToLevel = 1LL * flowers[left] * (left + 1) - prefixSum[left + 1];
53 // Remaining flowers after leveling
54 long long remainingFlowers = newFlowers - costToLevel;
55 // Maximum height we can achieve by distributing remaining flowers evenly
56 long long achievableHeight = flowers[left] + remainingFlowers / (left + 1);
57 // Cap at (target - 1) since we want incomplete gardens
58 long long maxAllowedHeight = target - 1;
59 maxIncompleteHeight = min(achievableHeight, maxAllowedHeight);
60 }
61
62 // Calculate beauty: full gardens contribute 'full' points each,
63 // incomplete gardens contribute 'partial' points per unit of minimum height
64 long long currentBeauty = 1LL * numFullGardens * full + 1LL * maxIncompleteHeight * partial;
65 maxBeauty = max(maxBeauty, currentBeauty);
66 }
67
68 return maxBeauty;
69 }
70};
71
1/**
2 * Calculates the maximum beauty value by optimally distributing new flowers
3 * @param flowers - Array of initial flower counts in each garden
4 * @param newFlowers - Number of new flowers available to plant
5 * @param target - Target number of flowers for a garden to be considered "full"
6 * @param full - Beauty value gained for each full garden
7 * @param partial - Beauty value gained for each flower in incomplete gardens (based on minimum)
8 * @returns Maximum achievable beauty value
9 */
10function maximumBeauty(
11 flowers: number[],
12 newFlowers: number,
13 target: number,
14 full: number,
15 partial: number,
16): number {
17 // Sort gardens by flower count in ascending order
18 flowers.sort((a, b) => a - b);
19 const gardenCount: number = flowers.length;
20
21 // Build prefix sum array for efficient range sum calculations
22 const prefixSum: number[] = Array(gardenCount + 1).fill(0);
23 for (let i = 1; i <= gardenCount; i++) {
24 prefixSum[i] = prefixSum[i - 1] + flowers[i - 1];
25 }
26
27 // Count gardens that are already full (have at least target flowers)
28 let fullGardenCount: number = flowers.filter(flowerCount => flowerCount >= target).length;
29 let maxBeauty: number = 0;
30
31 // Try different numbers of full gardens, starting from current count
32 for (; fullGardenCount <= gardenCount; ++fullGardenCount) {
33 // Calculate flowers needed to make the rightmost gardens full
34 if (fullGardenCount > 0) {
35 const flowersNeeded: number = Math.max(target - flowers[gardenCount - fullGardenCount], 0);
36 newFlowers -= flowersNeeded;
37 }
38
39 // If not enough flowers to achieve this configuration, stop
40 if (newFlowers < 0) {
41 break;
42 }
43
44 // Binary search to find how many incomplete gardens we can raise to the same level
45 let left: number = 0;
46 let right: number = gardenCount - fullGardenCount - 1;
47
48 while (left < right) {
49 const mid: number = (left + right + 1) >> 1;
50 // Calculate cost to raise gardens [0, mid] to the level of garden[mid]
51 const costToLevel: number = flowers[mid] * (mid + 1) - prefixSum[mid + 1];
52
53 if (costToLevel <= newFlowers) {
54 left = mid;
55 } else {
56 right = mid - 1;
57 }
58 }
59
60 // Calculate the minimum flower count in incomplete gardens
61 let minIncompleteFlowers: number = 0;
62
63 if (right !== -1) {
64 // Cost to raise gardens [0, left] to the level of garden[left]
65 const levelingCost: number = flowers[left] * (left + 1) - prefixSum[left + 1];
66 // Calculate how high we can raise all incomplete gardens uniformly
67 const remainingFlowers: number = newFlowers - levelingCost;
68 const gardensToRaise: number = left + 1;
69 minIncompleteFlowers = Math.min(
70 flowers[left] + Math.floor(remainingFlowers / gardensToRaise),
71 target - 1 // Cannot exceed target - 1 for incomplete gardens
72 );
73 }
74
75 // Calculate beauty for this configuration and update maximum
76 const currentBeauty: number = fullGardenCount * full + minIncompleteFlowers * partial;
77 maxBeauty = Math.max(maxBeauty, currentBeauty);
78 }
79
80 return maxBeauty;
81}
82
Time and Space Complexity
Time Complexity: O(n × log n)
The time complexity is dominated by:
- Initial sorting of the flowers array:
O(n log n)
- Creating the prefix sum array using accumulate:
O(n)
- Binary search to find initial value of
i
:O(log n)
- The main loop runs at most
n + 1
iterations (fromi
ton
) - Inside each iteration:
- Calculating
max(target - flowers[n - x], 0)
:O(1)
- Binary search loop to find the optimal position:
O(log n)
- Other operations are
O(1)
- Calculating
Therefore, the overall time complexity is O(n log n) + O(n) × O(log n) = O(n log n)
.
Space Complexity: O(n)
The space complexity comes from:
- The prefix sum array
s
created byaccumulate
:O(n)
- Other variables (
ans
,i
,l
,r
,mid
,x
,y
,cost
) use constant space:O(1)
Therefore, the overall space complexity is O(n)
.
Learn more about how to find time and space complexity quickly.
Common Pitfalls
1. Not Handling the Case When All Gardens Become Complete
The Pitfall: When calculating the partial beauty component, if all gardens become complete (i.e., num_complete == n
), there are no incomplete gardens left. The code tries to binary search on an empty range with right = n - num_complete - 1 = -1
, which causes incorrect behavior. The binary search loop condition left < right
with left = 0, right = -1
immediately exits, and right = -1
is used to determine partial_level = 0
. While this happens to give the correct result, it's based on coincidental behavior rather than explicit handling.
Why It Happens: The algorithm doesn't explicitly check if there are any incomplete gardens remaining before attempting to optimize their minimum flower count.
Solution: Add an explicit check for when all gardens are complete:
# After calculating newFlowers for this configuration
if num_complete == n:
# All gardens are complete, no partial component
beauty_score = num_complete * full
max_beauty = max(max_beauty, beauty_score)
continue
# Only perform binary search if there are incomplete gardens
left, right = 0, n - num_complete - 1
# ... rest of the binary search logic
2. Integer Overflow in Cost Calculation
The Pitfall: When calculating flowers[mid] * (mid + 1) - prefix_sum[mid + 1]
, the multiplication can cause integer overflow for large values, especially in languages with fixed integer sizes. Even in Python (which has arbitrary precision), this can lead to performance issues with very large numbers.
Why It Happens: The cost calculation involves multiplying potentially large flower counts by the number of gardens, which can exceed typical integer limits.
Solution: Check for potential overflow or use appropriate data types:
# Add bounds checking or use careful calculation cost_to_level = flowers[mid] * (mid + 1) - prefix_sum[mid + 1] if cost_to_level > newFlowers: right = mid - 1 continue
3. Incorrect Handling of Already Complete Gardens
The Pitfall: When a garden at position n - num_complete
already has target
or more flowers, the code correctly calculates max(target - flowers[n - num_complete], 0)
which gives 0. However, if not careful with the iteration bounds, you might try to "complete" gardens that are already complete, wasting the iteration.
Why It Happens: The initial count of gardens_already_full
finds how many gardens are already at or above target, but the loop still processes these configurations.
Solution: Ensure the loop starts from the correct position and handles the initial state properly:
# More explicit handling
for num_complete in range(gardens_already_full, n + 1):
if num_complete > gardens_already_full:
# Only need to add flowers if we're completing more than already complete
flowers_needed = max(target - flowers[n - num_complete], 0)
newFlowers -= flowers_needed
4. Not Resetting newFlowers
Between Iterations
The Pitfall: The code modifies newFlowers
in each iteration by subtracting the cost to complete gardens. If not careful, this modification persists across iterations, causing incorrect calculations for subsequent configurations.
Why It Happens: The variable is being reused and modified in place without proper restoration.
Solution: Use a temporary variable for each iteration:
for num_complete in range(gardens_already_full, n + 1):
remaining_flowers = newFlowers # Create a copy for this iteration
if num_complete > 0:
flowers_needed = max(target - flowers[n - num_complete], 0)
remaining_flowers -= flowers_needed
if remaining_flowers < 0:
break
# Use remaining_flowers for the rest of the calculations
Which two pointer techniques do you use to check if a string is a palindrome?
Recommended Readings
Greedy Introduction div class responsive iframe iframe src https www youtube com embed WTslqPbj7I title YouTube video player frameborder 0 allow accelerometer autoplay clipboard write encrypted media gyroscope picture in picture web share allowfullscreen iframe div When do we use greedy Greedy algorithms tend to solve optimization problems Typically they will ask you to calculate the max min of some value Commonly you may see this phrased in the problem as max min longest shortest largest smallest etc These keywords can be identified by just scanning
Tech Interview Pattern Two Pointers Introduction If you prefer videos here's a super quick introduction to Two Pointers div class responsive iframe iframe src https www youtube com embed xZ4AfXHQ1VQ title YouTube video player frameborder 0 allow accelerometer autoplay clipboard write encrypted media gyroscope picture in picture allowfullscreen iframe div Two pointers is a common interview
Binary Search Speedrun For each of the Speedrun questions you will be given a binary search related problem and a corresponding multiple choice question The multiple choice questions are related to the techniques and template s introduced in the binary search section It's recommended that you have gone through at
Want a Structured Path to Master System Design Too? Don’t Miss This!