Given an array arr[] of positive integers, find the minimum possible sum of a subsequence such that every subarray of four consecutive elements contains at least one element from the chosen subsequence.
Return the minimum possible sum.
Examples:
Input: arr[] = [1, 2, 3, 4, 5, 6, 7, 8]
Output: 6
Explanation: Choosing the subsequence [1, 5] gives a total sum of 6.
Every subarray of four consecutive elements contains at least one of these selected elements:
[1, 2, 3, 4] contains 1
[2, 3, 4, 5] contains 5
[3, 4, 5, 6] contains 5
[4, 5, 6, 7] contains 5
[5, 6, 7, 8] contains 5
Hence, the minimum possible sum is 6.Input: arr[] = [2, 1, 3]
Output: 1
Explanation: Since the array has fewer than four elements, selecting the minimum element (1) satisfies the condition.
Table of Content
[Naive Approach] Using Recursion - O(4^n) Time and O(n) Space
The idea is to recursively decide, starting from index 0, which of the next four elements to pick as the chosen one - since skipping all four would leave a window of 4 with no pick at all. Each pick moves the "covered up to here" pointer just past itself, and the recursion repeats on whatever remains. Once fewer than 4 elements are left after the last pick, no full window can form anymore, so that branch costs nothing.
- If the array has fewer than 4 elements, return the minimum element, as no window of size 4 exists.
- Consider the first uncovered window of 4 consecutive elements.
- Since this window must contain a chosen element, try picking each of its four elements.
- Add the chosen element's value and continue from the next index after it.
- If fewer than 4 elements remain, no further window needs to be covered, so contribute 0.
- Return the minimum cost among the four choices.
- Start this process from index 0 to obtain the final answer.
#include <iostream>
#include <vector>
#include <climits>
#include <algorithm>
using namespace std;
int findSum(vector<int> &arr, int i) {
int n = arr.size();
// No complete window of 4 remains, so no constraint left to satisfy
if (n - i <= 3) return 0;
int best = INT_MAX;
// Try picking each of the next 4 elements as the next chosen element
for (int j = i; j <= i + 3; j++) {
best = min(best, arr[j] + findSum(arr, j + 1));
}
return best;
}
int minSum(vector<int> &arr) {
int n = arr.size();
// Fewer than 4 elements: no window exists, so just take the minimum element
if (n < 4) return *min_element(arr.begin(), arr.end());
return findSum(arr, 0);
}
int main() {
vector<int> arr = {1, 2, 3, 4, 5, 6, 7, 8};
cout << minSum(arr) << endl;
return 0;
}
import java.util.Arrays;
class GFG {
static int findSum(int[] arr, int i) {
int n = arr.length;
// No complete window of 4 remains, so no constraint left to satisfy
if (n - i <= 3) return 0;
int best = Integer.MAX_VALUE;
// Try picking each of the next 4 elements as the next chosen element
for (int j = i; j <= i + 3; j++) {
best = Math.min(best, arr[j] + findSum(arr, j + 1));
}
return best;
}
static int minSum(int[] arr) {
int n = arr.length;
// Fewer than 4 elements: no window exists, so just take the minimum element
if (n < 4) return Arrays.stream(arr).min().getAsInt();
return findSum(arr, 0);
}
public static void main(String[] args) {
int[] arr = {1, 2, 3, 4, 5, 6, 7, 8};
System.out.println(minSum(arr));
}
}
def findSum(arr, i):
n = len(arr)
# No complete window of 4 remains, so no constraint left to satisfy
if n - i <= 3:
return 0
best = float('inf')
# Try picking each of the next 4 elements as the next chosen element
for j in range(i, i + 4):
best = min(best, arr[j] + findSum(arr, j + 1))
return best
def minSum(arr):
n = len(arr)
# Fewer than 4 elements: no window exists, so just take the minimum element
if n < 4:
return min(arr)
return findSum(arr, 0)
arr = [1, 2, 3, 4, 5, 6, 7, 8]
print(minSum(arr))
using System;
using System.Linq;
class GFG {
static int findSum(int[] arr, int i) {
int n = arr.Length;
// No complete window of 4 remains, so no constraint left to satisfy
if (n - i <= 3) return 0;
int best = int.MaxValue;
// Try picking each of the next 4 elements as the next chosen element
for (int j = i; j <= i + 3; j++) {
best = Math.Min(best, arr[j] + findSum(arr, j + 1));
}
return best;
}
static int minSum(int[] arr) {
int n = arr.Length;
// Fewer than 4 elements: no window exists, so just take the minimum element
if (n < 4) return arr.Min();
return findSum(arr, 0);
}
static void Main() {
int[] arr = {1, 2, 3, 4, 5, 6, 7, 8};
Console.WriteLine(minSum(arr));
}
}
function findSum(arr, i) {
const n = arr.length;
// No complete window of 4 remains, so no constraint left to satisfy
if (n - i <= 3) return 0;
let best = Infinity;
// Try picking each of the next 4 elements as the next chosen element
for (let j = i; j <= i + 3; j++) {
best = Math.min(best, arr[j] + findSum(arr, j + 1));
}
return best;
}
function minSum(arr) {
const n = arr.length;
// Fewer than 4 elements: no window exists, so just take the minimum element
if (n < 4) return Math.min(...arr);
return findSum(arr, 0);
}
const arr = [1, 2, 3, 4, 5, 6, 7, 8];
console.log(minSum(arr));
Output
6
[Better Approach] Dynamic Programming with Tabulation - O(n) Time and O(n) Space
Instead of exploring all subsets, build the answer incrementally using Dynamic Programming. Let dp[i] denote the minimum sum if arr[i] is chosen as the current pick. For the first 4 positions, dp[i] = arr[i]. For every other position, dp[i] = arr[i] + min(dp[i-1], dp[i-2], dp[i-3], dp[i-4]), since the previous pick must lie within the last 4 positions to avoid leaving a gap. The answer is the minimum dp value among the last 4 positions, ensuring the final window is also covered.
Let's understand with an example:
- Take arr = [1, 2, 3, 4, 5, 6, 7, 8].
- dp[0] = 1, dp[1] = 2, dp[2] = 3, dp[3] = 4 (each can be the first pick, so no addition needed).
- dp[4] = arr[4] + min(dp[0..3]) = 5 + min(1, 2, 3, 4) = 5 + 1 = 6.
- dp[5] = arr[5] + min(dp[1..4]) = 6 + min(2, 3, 4, 6) = 6 + 2 = 8.
- dp[6] = arr[6] + min(dp[2..5]) = 7 + min(3, 4, 6, 8) = 7 + 3 = 10.
- dp[7] = arr[7] + min(dp[3..6]) = 8 + min(4, 6, 8, 10) = 8 + 4 = 12.
- The answer is the minimum among the last 4 values: min(dp[4], dp[5], dp[6], dp[7]) = min(6, 8, 10, 12) = 6.
#include <iostream>
#include <vector>
#include <climits>
#include <algorithm>
using namespace std;
int minSum(vector<int> &arr) {
int n = arr.size();
int k = 4;
// fewer than k elements means one pick alone can cover every possible window
if (n < k)
return *min_element(arr.begin(), arr.end());
vector<int> dp(n);
for (int i = 0; i < n; i++) {
if (i < k) {
// can be the first pick, no earlier window to worry about
dp[i] = arr[i];
} else {
// add the smallest dp value among the previous k positions
int minPrev = INT_MAX;
for (int j = i - k; j < i; j++)
minPrev = min(minPrev, dp[j]);
dp[i] = arr[i] + minPrev;
}
}
// the last k positions form the final window, one of them must be picked
int result = INT_MAX;
for (int i = n - k; i < n; i++)
result = min(result, dp[i]);
return result;
}
int main() {
vector<int> arr = {1, 2, 3, 4, 5, 6, 7, 8};
cout << minSum(arr) << endl;
return 0;
}
class GFG {
static int minSum(int[] arr) {
int n = arr.length;
int k = 4;
// fewer than k elements means one pick alone can cover every possible window
if (n < k) {
int minVal = arr[0];
for (int v : arr) minVal = Math.min(minVal, v);
return minVal;
}
int[] dp = new int[n];
for (int i = 0; i < n; i++) {
if (i < k) {
// can be the first pick, no earlier window to worry about
dp[i] = arr[i];
} else {
// add the smallest dp value among the previous k positions
int minPrev = Integer.MAX_VALUE;
for (int j = i - k; j < i; j++)
minPrev = Math.min(minPrev, dp[j]);
dp[i] = arr[i] + minPrev;
}
}
// the last k positions form the final window, one of them must be picked
int result = Integer.MAX_VALUE;
for (int i = n - k; i < n; i++)
result = Math.min(result, dp[i]);
return result;
}
public static void main(String[] args) {
int[] arr = {1, 2, 3, 4, 5, 6, 7, 8};
System.out.println(minSum(arr));
}
}
def minSum(arr):
n = len(arr)
k = 4
# fewer than k elements means one pick alone can cover every possible window
if n < k:
return min(arr)
dp = [0] * n
for i in range(n):
if i < k:
# can be the first pick, no earlier window to worry about
dp[i] = arr[i]
else:
# add the smallest dp value among the previous k positions
dp[i] = arr[i] + min(dp[i - k:i])
# the last k positions form the final window, one of them must be picked
return min(dp[n - k:])
arr = [1, 2, 3, 4, 5, 6, 7, 8]
print(minSum(arr))
using System;
using System.Linq;
class GFG {
static int minSum(int[] arr) {
int n = arr.Length;
int k = 4;
// fewer than k elements means one pick alone can cover every possible window
if (n < k)
return arr.Min();
int[] dp = new int[n];
for (int i = 0; i < n; i++) {
if (i < k) {
// can be the first pick, no earlier window to worry about
dp[i] = arr[i];
} else {
// add the smallest dp value among the previous k positions
int minPrev = int.MaxValue;
for (int j = i - k; j < i; j++)
minPrev = Math.Min(minPrev, dp[j]);
dp[i] = arr[i] + minPrev;
}
}
// the last k positions form the final window, one of them must be picked
int result = int.MaxValue;
for (int i = n - k; i < n; i++)
result = Math.Min(result, dp[i]);
return result;
}
static void Main() {
int[] arr = { 1, 2, 3, 4, 5, 6, 7, 8 };
Console.WriteLine(minSum(arr));
}
}
function minSum(arr) {
const n = arr.length;
const k = 4;
// fewer than k elements means one pick alone can cover every possible window
if (n < k)
return Math.min(...arr);
const dp = new Array(n).fill(0);
for (let i = 0; i < n; i++) {
if (i < k) {
// can be the first pick, no earlier window to worry about
dp[i] = arr[i];
} else {
// add the smallest dp value among the previous k positions
let minPrev = Infinity;
for (let j = i - k; j < i; j++)
minPrev = Math.min(minPrev, dp[j]);
dp[i] = arr[i] + minPrev;
}
}
// the last k positions form the final window, one of them must be picked
let result = Infinity;
for (let i = n - k; i < n; i++)
result = Math.min(result, dp[i]);
return result;
}
// driver code
const arr = [1, 2, 3, 4, 5, 6, 7, 8];
console.log(minSum(arr));
Output
6
[Expected Approach] Using Space Optimized DP - O(n) Time and O(1) Space
The idea is to build the answer left to right, where the cost of validly covering everything up to a position is that element's value plus the smallest covering-cost among the previous four positions. Since each new position only ever depends on the last four results, there's no need to store the entire history - just keep sliding a window of 4 running totals forward as the array is scanned once. The small-array case is handled separately upfront, since no window constraint exists there at all.
- If the array has fewer than 4 elements, return the minimum element, as no window of size 4 exists.
- Maintain the minimum cost of ending the chosen subsequence at each position.
- For every element, add its value to the minimum cost among the previous four positions.
- Since only the last 4 DP values are needed, keep them in four variables instead of an array.
- After processing the current element, update these four values by shifting them forward.
- After scanning the entire array, the answer is the minimum among the last four DP values, as the final chosen element can lie in any of the last four positions.
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
int minSum(vector<int> &arr) {
int n = arr.size();
// Fewer than 4 elements: no window exists, so just take the minimum element
if (n < 4) return *min_element(arr.begin(), arr.end());
int dp1 = 0, dp2 = 0, dp3 = 0, dp4 = 0;
for (int i = 0; i < n; i++) {
int cur = arr[i] + min({dp1, dp2, dp3, dp4});
dp4 = dp3;
dp3 = dp2;
dp2 = dp1;
dp1 = cur;
}
return min({dp1, dp2, dp3, dp4});
}
int main() {
vector<int> arr = {1, 2, 3, 4, 5, 6, 7, 8};
cout << minSum(arr) << endl;
return 0;
}
import java.util.Arrays;
class GFG {
static int minSum(int[] arr) {
int n = arr.length;
// Fewer than 4 elements: no window exists, so just take the minimum element
if (n < 4) return Arrays.stream(arr).min().getAsInt();
int dp1 = 0, dp2 = 0, dp3 = 0, dp4 = 0;
for (int i = 0; i < n; i++) {
int cur = arr[i] + Math.min(Math.min(dp1, dp2), Math.min(dp3, dp4));
dp4 = dp3;
dp3 = dp2;
dp2 = dp1;
dp1 = cur;
}
return Math.min(Math.min(dp1, dp2), Math.min(dp3, dp4));
}
public static void main(String[] args) {
int[] arr = {1, 2, 3, 4, 5, 6, 7, 8};
System.out.println(minSum(arr));
}
}
def minSum(arr):
n = len(arr)
# Fewer than 4 elements: no window exists, so just take the minimum element
if n < 4:
return min(arr)
dp1 = dp2 = dp3 = dp4 = 0
for i in range(n):
cur = arr[i] + min(dp1, dp2, dp3, dp4)
dp4 = dp3
dp3 = dp2
dp2 = dp1
dp1 = cur
return min(dp1, dp2, dp3, dp4)
arr = [1, 2, 3, 4, 5, 6, 7, 8]
print(minSum(arr))
using System;
using System.Linq;
class GFG {
static int minSum(int[] arr) {
int n = arr.Length;
// Fewer than 4 elements: no window exists, so just take the minimum element
if (n < 4) return arr.Min();
int dp1 = 0, dp2 = 0, dp3 = 0, dp4 = 0;
for (int i = 0; i < n; i++) {
int cur = arr[i] + Math.Min(Math.Min(dp1, dp2), Math.Min(dp3, dp4));
dp4 = dp3;
dp3 = dp2;
dp2 = dp1;
dp1 = cur;
}
return Math.Min(Math.Min(dp1, dp2), Math.Min(dp3, dp4));
}
static void Main() {
int[] arr = {1, 2, 3, 4, 5, 6, 7, 8};
Console.WriteLine(minSum(arr));
}
}
function minSum(arr) {
const n = arr.length;
// Fewer than 4 elements: no window exists, so just take the minimum element
if (n < 4) return Math.min(...arr);
let dp1 = 0, dp2 = 0, dp3 = 0, dp4 = 0;
for (let i = 0; i < n; i++) {
const cur = arr[i] + Math.min(dp1, dp2, dp3, dp4);
dp4 = dp3;
dp3 = dp2;
dp2 = dp1;
dp1 = cur;
}
return Math.min(dp1, dp2, dp3, dp4);
}
// Driver Code
const arr = [1, 2, 3, 4, 5, 6, 7, 8];
console.log(minSum(arr));
Output
6