2971. Find Polygon With the Largest Perimeter
Problem Description
You are given an array of positive integers nums
of length n
. Your task is to find the largest possible perimeter of a polygon that can be formed using some or all of the numbers from the array as side lengths.
A polygon must have at least 3 sides, and for any valid polygon, the longest side must be strictly smaller than the sum of all other sides. This is known as the polygon inequality condition.
The problem states an important property: if you have k
positive numbers (where k ≥ 3
) arranged in non-decreasing order as a₁ ≤ a₂ ≤ a₃ ≤ ... ≤ aₖ
, and if the sum of the first k-1
numbers is greater than the last number (a₁ + a₂ + ... + aₖ₋₁ > aₖ
), then you can always form a valid polygon with these k
sides.
The perimeter of a polygon is simply the sum of all its side lengths.
Your goal is to:
- Select sides from the given array to form a valid polygon
- Maximize the perimeter of this polygon
- Return the maximum perimeter, or
-1
if no valid polygon can be formed
For example:
- If
nums = [1, 2, 3, 4, 5]
, you can form a polygon with sides[2, 3, 4, 5]
since2 + 3 + 4 = 9 > 5
, giving a perimeter of14
- If
nums = [1, 2, 10]
, no valid polygon can be formed since1 + 2 = 3
is not greater than10
, so return-1
Intuition
To maximize the perimeter, we want to include as many sides as possible with the largest values. The key insight is that if we can form a valid polygon with k
sides, we should check if we can form one with k+1
sides by adding another side, as this would give us a larger perimeter.
Let's think about when a set of sides can form a valid polygon. According to the problem, if we have sides in sorted order a₁ ≤ a₂ ≤ ... ≤ aₖ
, we need a₁ + a₂ + ... + aₖ₋₁ > aₖ
. This means the sum of all smaller sides must exceed the largest side.
This suggests a greedy approach: sort the array first. Why? Because:
- We can easily identify the largest side in any subset (it's the last element)
- We can efficiently check the polygon inequality condition
- We can systematically try different polygon sizes
Once sorted, we can try forming polygons of increasing sizes. For a polygon with k
sides using the first k
elements of the sorted array, we check if sum(first k-1 elements) > kth element
. If this condition holds, we have a valid polygon with perimeter equal to the sum of all k
elements.
The beauty of this approach is that we can use prefix sums to efficiently calculate the sum of any subset. By maintaining a running sum as we iterate through possible polygon sizes, we avoid recalculating sums repeatedly.
We start checking from size 3 (minimum polygon size) and go up to n
(using all elements). Each time we find a valid polygon, we update our answer with its perimeter if it's larger than what we've found so far. This ensures we find the maximum possible perimeter.
Learn more about Greedy, Prefix Sum and Sorting patterns.
Solution Approach
The solution implements the greedy approach with prefix sums for efficiency:
-
Sort the array: First, we sort
nums
in ascending order. This allows us to easily identify the largest side in any subset and check the polygon inequality. -
Build prefix sum array: We create a prefix sum array
s
usingaccumulate(nums, initial=0)
. This gives us:s[0] = 0
s[1] = nums[0]
s[2] = nums[0] + nums[1]
s[k] = nums[0] + nums[1] + ... + nums[k-1]
This allows us to get the sum of the first
k
elements in O(1) time. -
Check all possible polygon sizes: We iterate through all possible polygon sizes from
k = 3
tok = len(nums)
:- For each size
k
, we consider using the firstk
elements of the sorted array - The largest side would be
nums[k-1]
(the last element in our subset) - The sum of all other sides would be
s[k-1]
(sum of firstk-1
elements)
- For each size
-
Validate polygon inequality: For each size
k
, we check ifs[k-1] > nums[k-1]
:- If true, we have a valid polygon with perimeter
s[k]
(sum of allk
elements) - We update our answer with
max(ans, s[k])
to keep track of the maximum perimeter
- If true, we have a valid polygon with perimeter
-
Return result: After checking all possible sizes, we return the maximum perimeter found, or
-1
if no valid polygon exists.
The algorithm runs in O(n log n) time due to sorting, with O(n) space for the prefix sum array. The key optimization is using prefix sums to avoid recalculating sums, making each validation check O(1) instead of O(k).
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 nums = [3, 6, 2, 3]
.
Step 1: Sort the array
- Original:
[3, 6, 2, 3]
- Sorted:
[2, 3, 3, 6]
Step 2: Build prefix sum array
s[0] = 0
s[1] = 2
s[2] = 2 + 3 = 5
s[3] = 2 + 3 + 3 = 8
s[4] = 2 + 3 + 3 + 6 = 14
So s = [0, 2, 5, 8, 14]
Step 3: Check all possible polygon sizes
Size k = 3 (using first 3 elements: [2, 3, 3])
- Largest side:
nums[2] = 3
- Sum of other sides:
s[2] = 5
- Check: Is
5 > 3
? YES ✓ - Valid polygon with perimeter =
s[3] = 8
- Update answer:
ans = 8
Size k = 4 (using all 4 elements: [2, 3, 3, 6])
- Largest side:
nums[3] = 6
- Sum of other sides:
s[3] = 8
- Check: Is
8 > 6
? YES ✓ - Valid polygon with perimeter =
s[4] = 14
- Update answer:
ans = 14
Step 4: Return result
- Maximum perimeter found:
14
The algorithm correctly identifies that we can form a quadrilateral with all four sides [2, 3, 3, 6], giving us the maximum possible perimeter of 14. Note how the prefix sum array allows us to quickly check the polygon inequality (sum of smaller sides > largest side) in constant time for each potential polygon size.
Solution Implementation
1from typing import List
2from itertools import accumulate
3
4class Solution:
5 def largestPerimeter(self, nums: List[int]) -> int:
6 # Sort the array in ascending order
7 nums.sort()
8
9 # Create prefix sum array with initial value 0
10 # prefix_sums[i] contains sum of first i elements
11 prefix_sums = list(accumulate(nums, initial=0))
12
13 # Initialize answer as -1 (no valid polygon found)
14 max_perimeter = -1
15
16 # Check for each possible polygon size k (minimum 3 sides)
17 for k in range(3, len(nums) + 1):
18 # For a valid polygon with k sides, sum of first k-1 sides
19 # must be greater than the k-th side (largest side)
20 if prefix_sums[k - 1] > nums[k - 1]:
21 # Update maximum perimeter if valid polygon found
22 max_perimeter = max(max_perimeter, prefix_sums[k])
23
24 return max_perimeter
25
1class Solution {
2 public long largestPerimeter(int[] nums) {
3 // Sort the array in ascending order
4 Arrays.sort(nums);
5
6 int n = nums.length;
7
8 // Create prefix sum array to store cumulative sums
9 // prefixSum[i] represents sum of first i elements
10 long[] prefixSum = new long[n + 1];
11
12 // Build prefix sum array
13 for (int i = 1; i <= n; i++) {
14 prefixSum[i] = prefixSum[i - 1] + nums[i - 1];
15 }
16
17 // Initialize result as -1 (no valid polygon found)
18 long maxPerimeter = -1;
19
20 // Check all possible polygons with k sides (minimum 3 sides for a polygon)
21 for (int k = 3; k <= n; k++) {
22 // For a valid polygon with k sides, the sum of k-1 smallest sides
23 // must be greater than the largest side (polygon inequality theorem)
24 if (prefixSum[k - 1] > nums[k - 1]) {
25 // Update maximum perimeter if current polygon is valid
26 maxPerimeter = Math.max(maxPerimeter, prefixSum[k]);
27 }
28 }
29
30 return maxPerimeter;
31 }
32}
33
1class Solution {
2public:
3 long long largestPerimeter(vector<int>& nums) {
4 // Sort the array in ascending order
5 sort(nums.begin(), nums.end());
6
7 int n = nums.size();
8
9 // Create prefix sum array to store cumulative sums
10 // prefixSum[i] represents sum of first i elements
11 vector<long long> prefixSum(n + 1);
12
13 // Build prefix sum array
14 for (int i = 1; i <= n; ++i) {
15 prefixSum[i] = prefixSum[i - 1] + nums[i - 1];
16 }
17
18 // Initialize result as -1 (invalid polygon)
19 long long maxPerimeter = -1;
20
21 // Try to form polygons with k sides (minimum 3 sides for a valid polygon)
22 for (int k = 3; k <= n; ++k) {
23 // Check polygon inequality: sum of all smaller sides > largest side
24 // prefixSum[k - 1] is sum of first (k-1) elements
25 // nums[k - 1] is the k-th element (largest in current subset)
26 if (prefixSum[k - 1] > nums[k - 1]) {
27 // Valid polygon found, update maximum perimeter
28 // prefixSum[k] is the total perimeter of k sides
29 maxPerimeter = max(maxPerimeter, prefixSum[k]);
30 }
31 }
32
33 return maxPerimeter;
34 }
35};
36
1/**
2 * Finds the largest perimeter of a polygon that can be formed from the given array of side lengths.
3 * A valid polygon requires that the sum of any sides is greater than the remaining side.
4 *
5 * @param nums - Array of positive integers representing potential side lengths
6 * @returns The largest possible perimeter, or -1 if no valid polygon can be formed
7 */
8function largestPerimeter(nums: number[]): number {
9 // Sort the array in ascending order to check polygon validity efficiently
10 nums.sort((a: number, b: number) => a - b);
11
12 const arrayLength: number = nums.length;
13
14 // Create prefix sum array to store cumulative sums
15 // prefixSum[i] represents the sum of first i elements
16 const prefixSum: number[] = Array(arrayLength + 1).fill(0);
17
18 // Build the prefix sum array
19 for (let i = 0; i < arrayLength; ++i) {
20 prefixSum[i + 1] = prefixSum[i] + nums[i];
21 }
22
23 let maxPerimeter: number = -1;
24
25 // Try to form polygons with k sides (minimum 3 sides for a triangle)
26 for (let sideCount = 3; sideCount <= arrayLength; ++sideCount) {
27 // Check if the sum of first (sideCount - 1) sides is greater than the largest side
28 // This ensures the polygon inequality holds for sorted array
29 if (prefixSum[sideCount - 1] > nums[sideCount - 1]) {
30 // Update maximum perimeter if current polygon is valid
31 maxPerimeter = Math.max(maxPerimeter, prefixSum[sideCount]);
32 }
33 }
34
35 return maxPerimeter;
36}
37
Time and Space Complexity
Time Complexity: O(n log n)
where n
is the length of the input array nums
.
- Sorting the array takes
O(n log n)
time - Creating the prefix sum array using
accumulate
takesO(n)
time - The for loop iterates from index 3 to n, which is
O(n)
iterations - Each iteration performs constant time operations (comparison and max update)
- Overall:
O(n log n) + O(n) + O(n) = O(n log n)
Space Complexity: O(n)
where n
is the length of the input array.
- The sorting operation may use
O(log n)
toO(n)
space depending on the implementation (Python's Timsort uses up toO(n)
in worst case) - The prefix sum list
s
storesn+1
elements, requiringO(n)
space - The variable
ans
usesO(1)
space - Overall:
O(n) + O(n) + O(1) = O(n)
Learn more about how to find time and space complexity quickly.
Common Pitfalls
1. Incorrect Polygon Validation Logic
Pitfall: A common mistake is checking if the longest side is greater than or equal to the sum of other sides, rather than checking if the sum of other sides is strictly greater than the longest side.
# WRONG: This reverses the inequality
if nums[k - 1] >= prefix_sums[k - 1]: # Incorrect condition
max_perimeter = max(max_perimeter, prefix_sums[k])
Solution: Always ensure the sum of smaller sides is strictly greater than the longest side:
# CORRECT: Sum of smaller sides must be greater than longest side
if prefix_sums[k - 1] > nums[k - 1]:
max_perimeter = max(max_perimeter, prefix_sums[k])
2. Forgetting to Sort the Array
Pitfall: Processing the array without sorting it first, which breaks the algorithm's assumption that we can identify the largest side easily.
# WRONG: Operating on unsorted array
def largestPerimeter(self, nums: List[int]) -> int:
# Missing nums.sort()
prefix_sums = list(accumulate(nums, initial=0))
# ... rest of code
Solution: Always sort the array first to ensure elements are in non-decreasing order:
# CORRECT: Sort before processing
nums.sort()
prefix_sums = list(accumulate(nums, initial=0))
3. Off-by-One Errors in Prefix Sum Indexing
Pitfall: Confusing the indexing of prefix sums, especially forgetting that prefix_sums[i]
contains the sum of the first i
elements (not including index i
).
# WRONG: Incorrect indexing if prefix_sums[k] > nums[k]: # Off-by-one error max_perimeter = prefix_sums[k + 1] # Another off-by-one error
Solution: Remember that with initial=0
, prefix_sums[k]
contains the sum of nums[0]
through nums[k-1]
:
# CORRECT: Proper indexing
if prefix_sums[k - 1] > nums[k - 1]: # Sum of first k-1 elements > k-th element
max_perimeter = max(max_perimeter, prefix_sums[k]) # Total sum of k elements
4. Starting Loop from Wrong Index
Pitfall: Starting the loop from k = 2
or k = 1
instead of k = 3
, forgetting that a polygon needs at least 3 sides.
# WRONG: Polygon needs at least 3 sides
for k in range(2, len(nums) + 1): # Starting from 2 is incorrect
# ...
Solution: Always start from k = 3
since that's the minimum number of sides for a polygon:
# CORRECT: Start from 3 sides minimum
for k in range(3, len(nums) + 1):
# ...
5. Not Handling Edge Cases
Pitfall: Not considering arrays with fewer than 3 elements, which cannot form any polygon.
Solution: The current implementation handles this naturally since the loop won't execute if len(nums) < 3
, returning -1
as expected. However, you could add an explicit check for clarity:
if len(nums) < 3:
return -1
Which of the following shows the order of node visit in a Breadth-first Search?
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
Sorting Summary Comparisons We presented quite a few sorting algorithms and it is essential to know the advantages and disadvantages of each one The basic algorithms are easy to visualize and easy to learn for beginner programmers because of their simplicity As such they will suffice if you don't know any advanced
Want a Structured Path to Master System Design Too? Don’t Miss This!