Optimize Divide an array into continuous subsequences of length no greater than k such that sum of maximum value of each subsequence is minimum
Master System Design with Codemia
Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.
Introduction
This partitioning problem asks for an optimal way to split an array into contiguous groups, each of length at most k, while minimizing the sum of the maximum element from each group. Because every choice for the last group leaves a smaller instance of the same problem on the prefix, dynamic programming is the natural solution.
The key observation is simple: if the final group ends at position i, then its length can be anywhere from 1 to k, and the best total cost is the best prefix cost plus the maximum value inside that last group.
Dynamic Programming Recurrence
Let dp[i] be the minimum cost for partitioning the first i elements of the array. Then:
- '
dp[0] = 0' - For
i > 0, consider every valid last segment lengthlenfrom1tok - The segment is
arr[i-len : i] - The transition is
dp[i] = min(dp[i-len] + max(arr[i-len : i]))
That recurrence is correct because every optimal partition has a final segment, and the prefix before that segment must itself be optimally partitioned.
Runnable Python Example
A direct implementation runs in O(nk) time and O(n) memory.
For this input, the optimum is 17, achieved by splitting as [10, 1, 2] and [7, 5, 3]. The segment maxima are 10 and 7, so the total is 17. The point is that the best partition is not always obvious by inspection, which is exactly why the DP is useful.
To see the chosen partition, keep a parent pointer.
Why Greedy Ideas Fail
A greedy rule such as “always extend the current segment while the maximum does not rise too much” is unreliable. Local decisions can force a bad future split.
For example, keeping a moderate value inside one segment may prevent you from grouping several larger values together later under a single maximum. Because the effect of a cut depends on future elements up to k positions away, greedy reasoning is too short-sighted.
Dynamic programming works because it explores all legal final segment lengths while reusing solved prefix costs.
Complexity and Edge Cases
The straightforward DP takes O(nk) time, which is already good enough for many practical sizes. The inner loop updates the maximum incrementally as it expands the candidate last segment, so no extra scan is needed for each transition.
Edge cases to test:
- '
k = 1, where every element must stand alone.' - '
k >= n, where the whole array may be one segment.' - Negative numbers, where the maximum is still defined but the minimum total can behave differently from positive-only examples.
- Repeated values, which should not break the recurrence.
Common Pitfalls
A common mistake is confusing this problem with the opposite objective, where you maximize the sum of segment maxima. The recurrence looks similar, but the optimization direction changes.
Another issue is recomputing max(arr[i-len : i]) from scratch each time. That inflates the runtime unnecessarily. Update the current maximum as you expand the window backward.
Developers also sometimes try a monotonic-queue optimization before validating the simpler DP. Unless n and k are very large, the direct O(nk) solution is usually easier to get correct.
Finally, do not use non-contiguous groups. The problem requires continuous subsequences, so sorting or grouping by value changes the problem entirely.
Summary
- Use dynamic programming because each answer depends on the best partition of a prefix.
- The recurrence is
dp[i] = min(dp[i-len] + max(last segment))for1 <= len <= k. - A direct implementation runs in
O(nk)time andO(n)space. - Greedy strategies are not reliable because cut decisions affect later segment options.
- Track parent pointers if you need the actual partition, not just the minimum cost.

