Maximum Total Beauty of the Gardens
HardPrompt
Alice is a caretaker of n gardens and she wants to plant flowers to maximize the total beauty of all her gardens.
You are given a 0-indexed integer array flowers of size n, where flowers[i] is the number of flowers already planted in the ith garden. Flowers that are already planted cannot be removed. You are then given another integer newFlowers, which is the maximum number of flowers that Alice can additionally plant. You are also given the integers target, full, and partial.
A garden is considered complete if it has at least target flowers. The total beauty of the gardens is then determined as the sum of the following:
- The number of complete gardens multiplied by
full. - The minimum number of flowers in any of the incomplete gardens multiplied by
partial. If there are no incomplete gardens, then this value will be0.
Return the maximum total beauty that Alice can obtain after planting at most newFlowers flowers.
Example 1:
Input: flowers = [1,3,1,1], newFlowers = 7, target = 6, full = 12, partial = 1
Output: 14
Explanation: Alice can plant
- 2 flowers in the 0th garden
- 3 flowers in the 1st garden
- 1 flower in the 2nd garden
- 1 flower in the 3rd garden
The gardens will then be [3,6,2,2]. She planted a total of 2 + 3 + 1 + 1 = 7 flowers.
There is 1 garden that is complete.
The minimum number of flowers in the incomplete gardens is 2.
Thus, the total beauty is 1 * 12 + 2 * 1 = 12 + 2 = 14.
No other way of planting flowers can obtain a total beauty higher than 14.Example 2:
Input: flowers = [2,4,5,3], newFlowers = 10, target = 5, full = 2, partial = 6
Output: 30
Explanation: Alice can plant
- 3 flowers in the 0th garden
- 0 flowers in the 1st garden
- 0 flowers in the 2nd garden
- 2 flowers in the 3rd garden
The gardens will then be [5,4,5,5]. She planted a total of 3 + 0 + 0 + 2 = 5 flowers.
There are 3 gardens that are complete.
The minimum number of flowers in the incomplete gardens is 4.
Thus, the total beauty is 3 * 2 + 4 * 6 = 6 + 24 = 30.
No other way of planting flowers can obtain a total beauty higher than 30.
Note that Alice could make all the gardens complete but in this case, she would obtain a lower total beauty.
Constraints:
1 <= flowers.length <= 1051 <= flowers[i], target <= 1051 <= newFlowers <= 10101 <= full, partial <= 105
Approaches
2 approaches with complexity analysis and trade-offs.
This approach systematically explores all possibilities by breaking down the problem. We first sort the flowers array to make optimal choices easily. The main idea is to iterate through every possible number of gardens we can make 'complete', from n down to 0. For each number of complete gardens, k, we calculate the minimum cost required by picking the k gardens that are already closest to the target. If we can afford this cost with our newFlowers, we use the remaining flowers to improve the 'partial' beauty component.
The partial beauty is determined by the minimum number of flowers in any incomplete garden. To maximize this minimum, we can level up a prefix of the sorted incomplete gardens. We iterate through all possible prefixes, calculate the maximum level they can be uniformly raised to with the remaining flowers, and find the best possible partial beauty. This process is repeated for each k, and we keep track of the maximum total beauty seen.
Algorithm
- Sort the
flowersarray in non-decreasing order. This helps in optimally choosing which gardens to make complete (the ones already having more flowers) and which ones to level up (the ones with fewer flowers). - Calculate the prefix sums of the sorted
flowersarray. This will allowO(1)calculation of the sum of flowers in any subarray. Uselongdata type to prevent overflow. - Iterate through the number of gardens to make complete, let's say
k, fromndown to0. - For each
k:- Calculate the cost to make the
kgardens with the most flowers complete. These areflowers[n-k], ..., flowers[n-1]. The cost isk * target - sum(flowers[n-k...n-1]). - If this cost exceeds
newFlowers, we cannot makek(or more) gardens complete, so we can stop iterating. - Otherwise, calculate the remaining flowers:
rem_flowers = newFlowers - cost. - Now, consider the
n-kincomplete gardens:flowers[0], ..., flowers[n-k-1]. We want to userem_flowersto maximize their minimum level. - Iterate through all possible prefixes of these incomplete gardens, from
j=0ton-k-1. For each prefixflowers[0...j], calculate the maximum levelLthey can all be raised to. This level isL = (rem_flowers + sum(flowers[0...j])) / (j+1). - Keep track of the maximum
Lfound among all prefixes for the currentk. Let this bemax_L. Note thatmax_Lcannot exceedtarget - 1ifk > 0. - The total beauty for this
kisk * full + max_L * partial. - Update the overall maximum beauty found so far.
- Calculate the cost to make the
- Handle the edge case where all
ngardens can be made complete. If so, the beauty isn * full.
Walkthrough
The algorithm begins by sorting the flowers array. This allows us to greedily decide which gardens to complete and which to level up. To make k gardens complete, it's always optimal to pick the k gardens that already have the most flowers, i.e., the last k gardens in the sorted array. To maximize the minimum of the incomplete gardens, it's best to raise the gardens with the fewest flowers, i.e., a prefix of the sorted array.
We loop through k from n down to 0, representing the number of gardens we will make complete. In each iteration, we calculate the cost. If we have enough newFlowers, we find the remaining flowers and then try to find the best partial score. The partial score is maximized by finding the highest possible minimum L among the n-k incomplete gardens. This is done via a second, nested loop that checks every possible prefix 0...j of the incomplete gardens and calculates the maximum level L it can be raised to. The total beauty is then calculated and compared with the global maximum.
import java.util.Arrays; class Solution { public long maximumBeauty(int[] flowers, long newFlowers, int target, int full, int partial) { int n = flowers.length; long[] sortedFlowers = new long[n]; for (int i = 0; i < n; i++) { sortedFlowers[i] = Math.min(flowers[i], target); } Arrays.sort(sortedFlowers); long[] prefixSum = new long[n + 1]; for (int i = 0; i < n; i++) { prefixSum[i + 1] = prefixSum[i] + sortedFlowers[i]; } long maxBeauty = 0; // Case with 0 complete gardens long maxLevel = 0; for (int j = 0; j < n; j++) { long cost = (j + 1) * sortedFlowers[j] - prefixSum[j + 1]; if (cost <= newFlowers) { long currentLevel = (newFlowers - cost) / (j + 1) + sortedFlowers[j]; maxLevel = Math.max(maxLevel, currentLevel); } } if (n > 0) { maxBeauty = Math.max(maxBeauty, Math.min(maxLevel, target - 1) * partial); } long costToComplete = 0; for (int i = n - 1; i >= 0; i--) { costToComplete += Math.max(0, target - sortedFlowers[i]); if (costToComplete > newFlowers) { break; } long remainingFlowers = newFlowers - costToComplete; int numComplete = n - i; int numIncomplete = i; maxLevel = 0; if (numIncomplete > 0) { // Find max level for incomplete gardens 0 to i-1 int low = 0, high = numIncomplete - 1, bestJ = -1; while(low <= high) { int mid = low + (high - low) / 2; if (sortedFlowers[mid] * (mid + 1) - prefixSum[mid + 1] <= remainingFlowers) { bestJ = mid; low = mid + 1; } else { high = mid - 1; } } if (bestJ != -1) { long cost = (bestJ + 1) * sortedFlowers[bestJ] - prefixSum[bestJ + 1]; maxLevel = (remainingFlowers - cost) / (bestJ + 1) + sortedFlowers[bestJ]; } } long currentBeauty = (long) numComplete * full; if (numIncomplete > 0) { currentBeauty += Math.min(maxLevel, target - 1) * partial; } maxBeauty = Math.max(maxBeauty, currentBeauty); } if (costToComplete <= newFlowers) { maxBeauty = Math.max(maxBeauty, (long)n * full); } return maxBeauty; }}Note: The provided code snippet is a slightly optimized O(N log N) version of the O(N^2) idea, using binary search to find the best prefix j instead of a linear scan. A pure O(N^2) implementation would be too slow.
Complexity
Time
O(n^2) due to the nested loops. The outer loop runs `n` times, and the inner loop can run up to `n` times. Sorting takes an initial `O(n log n)`.
Space
O(n) to store the sorted array and prefix sums.
Trade-offs
Pros
The logic is straightforward to understand.
It correctly explores the trade-off between making more gardens complete versus raising the minimum level of incomplete ones.
Cons
The nested loop structure leads to a quadratic time complexity, which is too slow for the given constraints.
This approach significantly improves upon the previous one by optimizing the search for the best partial beauty component. The overall structure remains the same: sort the flowers and iterate through the number of gardens, k, to make complete. However, for each k, instead of a linear scan to find the best minimum level L for the incomplete gardens, we use binary search.
The key insight is that the feasibility of achieving a certain minimum level L is a monotonic function. If we have enough flowers to ensure all incomplete gardens have at least L flowers, we certainly have enough to ensure they all have L-1 flowers. This allows us to binary search for the maximum possible L in the range [0, target-1].
To check if a given level L is achievable within our remaining flower budget, we need to calculate the cost. This involves finding all incomplete gardens with fewer than L flowers and summing up the flowers needed to raise them to L. This sub-problem can also be solved efficiently using another binary search (upper_bound) on the sorted array of incomplete gardens to find the boundary. This nested binary search structure leads to a much better time complexity.
Algorithm
- Sort the
flowersarray and compute prefix sums, similar to the previous approach. Uselongfor sums and costs to avoid overflow. It's beneficial to cap flower counts attargetsince any excess is wasted. - Initialize
max_beauty. A good starting point is to calculate the beauty for the case of 0 complete gardens. This involves finding the max levelLachievable using allnewFlowerson allngardens. ThisLcan be found using binary search. - Iterate through the number of gardens to make complete,
k, fromndown to1. - For each
k:- Calculate the cost to make the
klargest gardens complete:cost_k = k * target - sum(flowers[n-k...n-1]). - If
cost_k > newFlowers, we cannot afford it, so break the loop. - Calculate
rem_flowers = newFlowers - cost_k. - The
n-kgardensflowers[0...n-k-1]are incomplete. We need to find the maximum possible minimum levelLfor this group usingrem_flowers. - This subproblem has a monotonic property: if level
Lis achievable, any levelL' < Lis also achievable. This allows for binary search on the answerL. - Perform a binary search for
Lin the range[0, target-1]. - The
check(L)function for this binary search determines ifmin_level = Lis possible. To do this:- Find the rightmost garden
pamong the incomplete ones such thatflowers[p] < L. This can be done efficiently with another binary search (upper_bound) onflowers[0...n-k-1]. - The cost to raise all gardens
0...pto levelLis(p+1) * L - sum(flowers[0...p]). - If this cost is
<= rem_flowers,check(L)returns true.
- Find the rightmost garden
- The result of the binary search is the maximum
Lfor the currentk. Let it bemax_L. - Calculate beauty
k * full + max_L * partialand updatemax_beauty.
- Calculate the cost to make the
- Finally, consider the case where all
ngardens are made complete, which gives a beauty ofn * fullif affordable, and updatemax_beautyone last time.
Walkthrough
After sorting flowers and calculating prefix sums, we iterate i from n-1 down to 0, where i is the index of the first garden we consider making complete. This means we are trying to make n-i gardens complete.
For each i, we calculate costToComplete. If it's within our newFlowers budget, we proceed. We are left with remainingFlowers and i incomplete gardens (flowers[0...i-1]).
Now, we must find the maximum possible value for the minimum flowers in these i gardens. We binary search for this value, L, in the range [0, target-1]. For a given L (our mid in the binary search), we check if it's possible to make all gardens in flowers[0...i-1] have at least L flowers. To do this, we find the rightmost garden p in this range with flowers[p] < L. This is done via a lower_bound or upper_bound search, which is another binary search. The cost to raise all gardens up to p to level L is (p+1)*L - prefixSum[p+1]. If this cost is not more than remainingFlowers, then L is achievable, and we try for a higher L. Otherwise, L is too high.
Once the optimal L is found for the current number of complete gardens, we calculate the total beauty and update our overall maximum. This process efficiently finds the optimal balance between complete and incomplete gardens.
import java.util.Arrays; class Solution { public long maximumBeauty(int[] flowers, long newFlowers, int target, int full, int partial) { int n = flowers.length; long[] sortedFlowers = new long[n]; for (int i = 0; i < n; i++) { sortedFlowers[i] = flowers[i]; } Arrays.sort(sortedFlowers); long[] prefixSum = new long[n + 1]; for (int i = 0; i < n; i++) { prefixSum[i + 1] = prefixSum[i] + sortedFlowers[i]; } long maxBeauty = 0; long costToComplete = 0; int j = n - 1; for (int i = n; i >= 0; i--) { long remainingFlowers; if (i < n) { costToComplete += Math.max(0, target - sortedFlowers[i]); } if (costToComplete > newFlowers) { break; } remainingFlowers = newFlowers - costToComplete; j = Math.min(j, i - 1); while (j >= 0 && (sortedFlowers[j] * (j + 1) - prefixSum[j + 1]) > remainingFlowers) { j--; } long currentBeauty = (long)(n - i) * full; if (j >= 0) { long level = Math.min(target - 1, sortedFlowers[j] + (remainingFlowers - (sortedFlowers[j] * (j + 1) - prefixSum[j + 1])) / (j + 1)); currentBeauty += level * partial; } maxBeauty = Math.max(maxBeauty, currentBeauty); } return maxBeauty; }}Note: The provided code snippet is a further optimized O(N log N) solution using a two-pointer approach, which is more efficient than the described nested binary search. The pointer j for the incomplete gardens does not need to be reset in each iteration of the main loop, as the remainingFlowers budget is non-decreasing.
Complexity
Time
O(n log n + n * log(target) * log n). Sorting takes `O(n log n)`. The main loop runs `n` times. Inside, a binary search takes `O(log target)` steps, and each step takes `O(log n)` for the inner search. The two-pointer optimization shown in the code reduces this to O(n log n).
Space
O(n) for storing the sorted array and prefix sums.
Trade-offs
Pros
Much more efficient and will pass within the time limits for the given constraints.
It's a standard and powerful technique for problems involving searching for an optimal value with a monotonic property.
Cons
The logic is more complex, involving a nested binary search structure.
Implementation requires careful handling of indices and
longdata types to avoid bugs and overflow.
Solutions
Solution
class Solution {public long maximumBeauty(int[] flowers, long newFlowers, int target, int full, int partial) { Arrays.sort(flowers); int n = flowers.length; long[] s = new long[n + 1]; for (int i = 0; i < n; ++i) { s[i + 1] = s[i] + flowers[i]; } long ans = 0; int x = 0; for (int v : flowers) { if (v >= target) { ++x; } } for (; x <= n; ++x) { newFlowers -= (x == 0 ? 0 : Math.max(target - flowers[n - x], 0)); if (newFlowers < 0) { break; } int l = 0, r = n - x - 1; while (l < r) { int mid = (l + r + 1) >> 1; if ((long)flowers[mid] * (mid + 1) - s[mid + 1] <= newFlowers) { l = mid; } else { r = mid - 1; } } long y = 0; if (r != -1) { long cost = (long)flowers[l] * (l + 1) - s[l + 1]; y = Math.min(flowers[l] + (newFlowers - cost) / (l + 1), target - 1); } ans = Math.max(ans, (long)x * full + y * partial); } return ans; }}class Solution {public: long long maximumBeauty(vector<int> &flowers, long long newFlowers, int target, int full, int partial) { sort(flowers.begin(), flowers.end()); int n = flowers.size(); long long s[n + 1]; s[0] = 0; for (int i = 1; i <= n; ++i) { s[i] = s[i - 1] + flowers[i - 1]; } long long ans = 0; int i = flowers.end() - lower_bound(flowers.begin(), flowers.end(), target); for (int x = i; x <= n; ++x) { newFlowers -= (x == 0 ? 0 : max(target - flowers[n - x], 0)); if (newFlowers < 0) { break; } int l = 0, r = n - x - 1; while (l < r) { int mid = (l + r + 1) >> 1; if (1LL * flowers[mid] * (mid + 1) - s[mid + 1] <= newFlowers) { l = mid; } else { r = mid - 1; } } int y = 0; if (r != -1) { long long cost = 1LL * flowers[l] * (l + 1) - s[l + 1]; long long mx = flowers[l] + (newFlowers - cost) / (l + 1); long long threshold = target - 1; y = min(mx, threshold); } ans = max(ans, 1LL * x * full + 1LL * y * partial); } return ans; }};class Solution: def maximumBeauty(self, flowers: List[int], newFlowers: int, target: int, full: int, partial: int) -> int: flowers . sort() n = len(flowers) s = list(accumulate(flowers, initial=0)) ans, i = 0, n - bisect_left(flowers, target) for x in range(i, n + 1): newFlowers -= 0 if x == 0 else max(target - flowers[n - x], 0) if newFlowers < 0: break l, r = 0, n - x - 1 while l < r: mid = (l + r + 1) >> 1 if flowers[mid] * (mid + 1) - s[mid + 1] <= newFlowers: l = mid else: r = mid - 1 y = 0 if r != - 1: cost = flowers[l] * (l + 1) - s[l + 1] y = min(flowers[l] + (newFlowers - cost) // (l + 1), target - 1) ans = max(ans, x * full + y * partial) return ansVideo walkthrough
Newsletter
One sharp idea, every week
System design and interview prep — short enough to finish.
No spam. Unsubscribe anytime.
Practice
Same difficulty — related problems to reinforce the pattern.