1788. Maximize the Beauty of the Garden 🔒
Problem Description
You have a garden with n
flowers arranged in a line, where each flower has an integer beauty value (which can be positive, negative, or zero). These beauty values are given in an array flowers
where flowers[i]
represents the beauty of the i-th
flower.
A garden is considered valid if:
- It contains at least two flowers
- The first and last flowers have the same beauty value
As a gardener, you can remove any flowers (or none at all) from the garden. Your goal is to create a valid garden with the maximum possible total beauty. The beauty of a garden is the sum of all beauty values of the remaining flowers.
For example, if you have flowers with beauty values [1, 2, 3, 1, 2]
, you could keep flowers at positions 0 and 3 (both with beauty value 1) along with any flowers between them, creating a valid garden since the first and last flowers both have beauty value 1.
The task is to find the maximum possible beauty sum of a valid garden after optimally removing flowers.
Note that when calculating the beauty of a garden, you sum up all the beauty values of the flowers that remain in the garden, including any negative values.
Intuition
The key insight is that a valid garden must start and end with flowers having the same beauty value. So we need to find pairs of flowers with identical beauty values and maximize the sum of all flowers between them (inclusive).
When we encounter a flower with beauty value v
at position j
, we want to check if we've seen this value before. If we have seen it at an earlier position i
, then flowers from position i
to j
can form a valid garden. The beauty of this garden would be the sum of all flower values from i
to j
.
However, there's a twist - we want to maximize the beauty, so between positions i
and j
, we should keep all positive beauty values but skip negative ones (since we're allowed to remove any flowers). This is where prefix sums become useful.
We can maintain a prefix sum array where s[i]
represents the sum of all non-negative beauty values from the start up to position i-1
. When we find a matching pair at positions i
and j
, the sum of all positive values between them would be s[j] - s[i+1]
. But we must also include the boundary flowers (with value v
) at positions i
and j
, giving us a total of s[j] - s[i+1] + v + v
.
The reason we only accumulate non-negative values in our prefix sum is strategic: negative beauty values between the matching pair would decrease our total, so we simply choose not to include them (remove them from the garden). But the boundary flowers must be kept regardless of their value since they define the validity of the garden.
By tracking the first occurrence of each beauty value in a hash table and continuously updating our maximum as we find matching pairs, we can efficiently find the optimal valid garden in a single pass through the array.
Learn more about Greedy and Prefix Sum patterns.
Solution Approach
We implement the solution using a hash table and prefix sum array to efficiently find the maximum beauty valid garden.
Data Structures:
- Hash table
d
: Records the first occurrence index of each beauty value - Prefix sum array
s
: Stores cumulative sum of non-negative beauty values - Variable
ans
: Tracks the maximum beauty found, initialized to negative infinity
Algorithm Steps:
-
Initialize structures: Create a prefix sum array
s
of sizen+1
(one extra for easier indexing), an empty hash tabled
, and setans = -inf
. -
Single pass through flowers: For each flower at position
i
with beauty valuev
:a. Check for matching pair: If
v
already exists in hash tabled
:- We found a valid garden from position
d[v]
toi
- Calculate beauty:
s[i] - s[d[v] + 1] + v * 2
s[i] - s[d[v] + 1]
gives sum of positive values between the pair (exclusive)v * 2
adds the two boundary flowers
- Update
ans
with the maximum value
b. First occurrence: If
v
is not ind
:- Record this position:
d[v] = i
c. Update prefix sum:
s[i + 1] = s[i] + max(v, 0)
- We only add non-negative values to skip negative beauty values in the middle
- We found a valid garden from position
-
Return result: After processing all flowers,
ans
contains the maximum possible beauty.
Key Insight: The formula s[i] - s[d[v] + 1] + v * 2
works because:
s[i]
contains sum of all positive values from start to positioni-1
s[d[v] + 1]
contains sum of all positive values from start to positiond[v]
- Their difference gives us positive values between
d[v]+1
andi-1
- Adding
v * 2
includes the mandatory boundary flowers at positionsd[v]
andi
This approach runs in O(n) time with O(n) space complexity, processing each flower exactly once while maintaining the necessary information to find optimal valid gardens.
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 the solution with flowers = [1, -2, 3, 1, 5]
.
Initialization:
s = [0, 0, 0, 0, 0, 0]
(prefix sum array of size n+1)d = {}
(empty hash table)ans = -inf
Processing each flower:
i = 0, v = 1:
- Check if 1 is in
d
: No - Add to hash table:
d[1] = 0
- Update prefix sum:
s[1] = s[0] + max(1, 0) = 0 + 1 = 1
- Current state:
d = {1: 0}
,s = [0, 1, 0, 0, 0, 0]
,ans = -inf
i = 1, v = -2:
- Check if -2 is in
d
: No - Add to hash table:
d[-2] = 1
- Update prefix sum:
s[2] = s[1] + max(-2, 0) = 1 + 0 = 1
- Current state:
d = {1: 0, -2: 1}
,s = [0, 1, 1, 0, 0, 0]
,ans = -inf
i = 2, v = 3:
- Check if 3 is in
d
: No - Add to hash table:
d[3] = 2
- Update prefix sum:
s[3] = s[2] + max(3, 0) = 1 + 3 = 4
- Current state:
d = {1: 0, -2: 1, 3: 2}
,s = [0, 1, 1, 4, 0, 0]
,ans = -inf
i = 3, v = 1:
- Check if 1 is in
d
: Yes! Found at position 0 - Calculate beauty of garden from position 0 to 3:
beauty = s[3] - s[0 + 1] + 1 * 2
beauty = 4 - 1 + 2 = 5
- This represents keeping flowers [1, 3, 1] (skipping the -2)
- Update
ans = max(-inf, 5) = 5
- Update prefix sum:
s[4] = s[3] + max(1, 0) = 4 + 1 = 5
- Current state:
d = {1: 0, -2: 1, 3: 2}
,s = [0, 1, 1, 4, 5, 0]
,ans = 5
i = 4, v = 5:
- Check if 5 is in
d
: No - Add to hash table:
d[5] = 4
- Update prefix sum:
s[5] = s[4] + max(5, 0) = 5 + 5 = 10
- Final state:
d = {1: 0, -2: 1, 3: 2, 5: 4}
,s = [0, 1, 1, 4, 5, 10]
,ans = 5
Result: The maximum beauty is 5, achieved by keeping flowers at positions 0, 2, and 3 with values [1, 3, 1].
Solution Implementation
1from typing import List
2from math import inf
3
4class Solution:
5 def maximumBeauty(self, flowers: List[int]) -> int:
6 # prefix_sum[i] stores the sum of all positive values from index 0 to i-1
7 prefix_sum = [0] * (len(flowers) + 1)
8
9 # first_occurrence stores the first occurrence index of each flower value
10 first_occurrence = {}
11
12 # Initialize maximum beauty to negative infinity
13 max_beauty = -inf
14
15 # Iterate through each flower
16 for index, flower_value in enumerate(flowers):
17 # If we've seen this flower value before, we can form a valid subarray
18 if flower_value in first_occurrence:
19 # Calculate beauty:
20 # - Add the flower value twice (for start and end positions)
21 # - Add sum of positive values between the two occurrences
22 first_index = first_occurrence[flower_value]
23 positive_sum_between = prefix_sum[index] - prefix_sum[first_index + 1]
24 current_beauty = positive_sum_between + flower_value * 2
25 max_beauty = max(max_beauty, current_beauty)
26 else:
27 # Record the first occurrence of this flower value
28 first_occurrence[flower_value] = index
29
30 # Update prefix sum: only add positive values
31 prefix_sum[index + 1] = prefix_sum[index] + max(flower_value, 0)
32
33 return max_beauty
34
1class Solution {
2 public int maximumBeauty(int[] flowers) {
3 int n = flowers.length;
4
5 // Prefix sum array to store cumulative sum of positive flower values
6 // prefixSum[i] represents sum of positive values from index 0 to i-1
7 int[] prefixSum = new int[n + 1];
8
9 // Map to store the first occurrence index of each flower type
10 Map<Integer, Integer> firstOccurrenceMap = new HashMap<>();
11
12 // Initialize the maximum beauty to minimum value
13 int maxBeauty = Integer.MIN_VALUE;
14
15 // Iterate through each flower in the array
16 for (int i = 0; i < n; i++) {
17 int currentFlowerValue = flowers[i];
18
19 // Check if we've seen this flower type before
20 if (firstOccurrenceMap.containsKey(currentFlowerValue)) {
21 // Calculate beauty: sum of flowers between first and current occurrence
22 // plus twice the flower value (for the two selected flowers)
23 int firstIndex = firstOccurrenceMap.get(currentFlowerValue);
24 int beautyValue = prefixSum[i] - prefixSum[firstIndex + 1] + currentFlowerValue * 2;
25 maxBeauty = Math.max(maxBeauty, beautyValue);
26 } else {
27 // Record the first occurrence of this flower type
28 firstOccurrenceMap.put(currentFlowerValue, i);
29 }
30
31 // Update prefix sum: only add positive values to maximize beauty
32 prefixSum[i + 1] = prefixSum[i] + Math.max(currentFlowerValue, 0);
33 }
34
35 return maxBeauty;
36 }
37}
38
1class Solution {
2public:
3 int maximumBeauty(vector<int>& flowers) {
4 int n = flowers.size();
5
6 // Prefix sum array to store cumulative sum of positive values
7 // prefixSum[i] represents sum of max(0, flowers[j]) for j < i
8 vector<int> prefixSum(n + 1, 0);
9
10 // Map to store the first occurrence index of each flower value
11 unordered_map<int, int> firstOccurrence;
12
13 int maxBeauty = INT_MIN;
14
15 for (int i = 0; i < n; ++i) {
16 int currentFlower = flowers[i];
17
18 // If we've seen this flower value before, calculate the beauty
19 // of the garden formed by using positions firstOccurrence[currentFlower] and i as endpoints
20 if (firstOccurrence.count(currentFlower)) {
21 int leftIndex = firstOccurrence[currentFlower];
22 // Beauty = sum of flowers between (exclusive) the two occurrences + 2 * flower value
23 // The sum excludes negative values (only positive values contribute)
24 int beautyValue = prefixSum[i] - prefixSum[leftIndex + 1] + currentFlower * 2;
25 maxBeauty = max(maxBeauty, beautyValue);
26 } else {
27 // Record the first occurrence of this flower value
28 firstOccurrence[currentFlower] = i;
29 }
30
31 // Update prefix sum: add current value if positive, otherwise add 0
32 prefixSum[i + 1] = prefixSum[i] + max(currentFlower, 0);
33 }
34
35 return maxBeauty;
36 }
37};
38
1/**
2 * Finds the maximum beauty of a flower garden path.
3 * Beauty is defined as the sum of flowers between two same-valued flowers (inclusive).
4 * Negative valued flowers between the two endpoints reduce the beauty.
5 *
6 * @param flowers - Array of integers representing flower values (can be negative)
7 * @returns Maximum beauty value possible, or -Infinity if no valid path exists
8 */
9function maximumBeauty(flowers: number[]): number {
10 const flowerCount: number = flowers.length;
11
12 // Prefix sum array to store cumulative sum of positive flowers
13 // prefixSum[i] represents sum of all positive flowers from index 0 to i-1
14 const prefixSum: number[] = Array(flowerCount + 1).fill(0);
15
16 // Map to store the first occurrence index of each flower value
17 const firstOccurrenceMap: Map<number, number> = new Map();
18
19 // Initialize maximum beauty to negative infinity
20 let maxBeauty: number = -Infinity;
21
22 // Iterate through each flower in the garden
23 for (let currentIndex = 0; currentIndex < flowerCount; ++currentIndex) {
24 const currentFlowerValue: number = flowers[currentIndex];
25
26 // Check if we've seen this flower value before
27 if (firstOccurrenceMap.has(currentFlowerValue)) {
28 // Calculate beauty: sum of flowers between first and current occurrence
29 // Formula: (sum of positive flowers between) + (2 * flower value at endpoints)
30 const firstIndex: number = firstOccurrenceMap.get(currentFlowerValue)!;
31 const beautyValue: number = prefixSum[currentIndex] - prefixSum[firstIndex + 1] + currentFlowerValue * 2;
32 maxBeauty = Math.max(maxBeauty, beautyValue);
33 } else {
34 // Record first occurrence of this flower value
35 firstOccurrenceMap.set(currentFlowerValue, currentIndex);
36 }
37
38 // Update prefix sum: only add positive flower values
39 prefixSum[currentIndex + 1] = prefixSum[currentIndex] + Math.max(currentFlowerValue, 0);
40 }
41
42 return maxBeauty;
43}
44
Time and Space Complexity
Time Complexity: O(n)
The algorithm iterates through the flowers array exactly once using a single for loop. Within each iteration, all operations are constant time:
- Dictionary lookup (
v in d
) -O(1)
average case - Dictionary insertion (
d[v] = i
) -O(1)
average case - Array access and update (
s[i + 1] = s[i] + max(v, 0)
) -O(1)
- Max comparisons -
O(1)
Since we perform n
iterations with O(1)
operations per iteration, the overall time complexity is O(n)
.
Space Complexity: O(n)
The algorithm uses additional space for:
- Array
s
of sizelen(flowers) + 1
-O(n)
- Dictionary
d
which in the worst case stores all unique flower values -O(n)
- Variables
ans
,i
,v
-O(1)
The dominant space usage comes from the array s
and dictionary d
, both of which can grow proportionally to the input size n
. Therefore, the total space complexity is O(n)
.
Learn more about how to find time and space complexity quickly.
Common Pitfalls
1. Including Negative Values in the Middle
Pitfall: A common mistake is trying to include all values between the matching flowers, including negative ones. Developers might write:
# WRONG: This includes negative values unnecessarily
current_beauty = sum(flowers[first_index:index+1])
Why it's wrong: Including negative values between the matching flowers reduces the total beauty unnecessarily. We should skip them since we're allowed to remove any flowers from the middle.
Solution: Use a prefix sum array that only accumulates non-negative values:
# Correct: Only sum positive values between the boundaries
prefix_sum[index + 1] = prefix_sum[index] + max(flower_value, 0)
positive_sum_between = prefix_sum[index] - prefix_sum[first_index + 1]
2. Off-by-One Errors in Index Calculation
Pitfall: Incorrectly calculating the sum between two positions:
# WRONG: This includes the value at first_index positive_sum_between = prefix_sum[index] - prefix_sum[first_index]
Why it's wrong: The prefix sum at prefix_sum[first_index]
contains values from 0 to first_index-1
. To get values strictly between first_index
and index
, we need prefix_sum[index] - prefix_sum[first_index + 1]
.
Solution: Use first_index + 1
to exclude the boundary flower from the middle sum:
# Correct: Excludes both boundary flowers from the middle sum positive_sum_between = prefix_sum[index] - prefix_sum[first_index + 1] current_beauty = positive_sum_between + flower_value * 2
3. Updating First Occurrence After Finding a Match
Pitfall: Updating the first occurrence of a flower value after finding a match:
# WRONG: Don't update first_occurrence after finding a match if flower_value in first_occurrence: # ... calculate beauty ... first_occurrence[flower_value] = index # WRONG!
Why it's wrong: We always want to use the leftmost occurrence to potentially create the longest valid garden. Updating it would miss better solutions.
Solution: Only update first_occurrence
when encountering a value for the first time:
# Correct: Only set first occurrence once if flower_value in first_occurrence: # Calculate beauty using the original first occurrence # ... else: first_occurrence[flower_value] = index # Only set on first encounter
4. Forgetting to Handle Negative Boundary Values
Pitfall: Assuming boundary flowers are always positive and forgetting to add them when they're negative:
# WRONG: Only works correctly for positive boundary values if flower_value > 0: current_beauty = positive_sum_between + flower_value * 2
Why it's wrong: Even if the boundary flowers have negative beauty values, they must be included in a valid garden. The algorithm should still consider them as they might lead to the maximum beauty when combined with large positive values in between.
Solution: Always include the boundary flowers regardless of their sign:
# Correct: Always add boundary flowers, even if negative current_beauty = positive_sum_between + flower_value * 2
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
Prefix Sum The prefix sum is an incredibly powerful and straightforward technique Its primary goal is to allow for constant time range sum queries on an array What is Prefix Sum The prefix sum of an array at index i is the sum of all numbers from index 0 to i By
Coding Interview Patterns Your Personal Dijkstra's Algorithm to Landing Your Dream Job The goal of AlgoMonster is to help you get a job in the shortest amount of time possible in a data driven way We compiled datasets of tech interview problems and broke them down by patterns This way
Want a Structured Path to Master System Design Too? Don’t Miss This!