Given an array arr[] of size n, a sequence is called valid if it is in series of blocks where each starting element denotes the length of the block i.e first comes the length of the block then its element, for example the sequence [3 3 4 5 2 6 1], here the blocks are [3 3 4 5] and [2 6 1]. In one operation you can remove any one element of the array. The task is to determine the minimum number of operations required to make the array a valid sequence.
Example:
Input: arr = [3 4 1 6 7 7]
Output: 1
Explanation: remove 3 then the array becomes [4 1 6 7 7]Input: arr = {4, 5, 5, 1, 5}
Output: 0
Approach:
The intuition behind the solution is to use dynamic programming to keep track of the minimum number of operations required to make the segment from i to n valid. Here, n is the size of the array, and i ranges from 1 to n (1-based indexing is considered here)
The dynamic programming approach involves creating an array dp[], where dp[i] represents the minimum number of operations required to make the segment from i to n valid.
There are two possible ways to make the segment from i to n valid:
- Remove the element at position i: If we remove the element at position i, then we need to make the segment from i+1 to n valid. The number of operations required for this is dp[i+1]+1, because we perform one operation (removal) plus whatever operations are needed to make the rest of the array valid.
- Skip to the end of the block starting at i: If the element at position i denotes the length of a block, we can skip to the end of this block and make the segment from i+a[i]+1 to n valid. The number of operations required for this is dp[i+a[i]+1], because we don’t perform any removals, but we need to make the rest of the array valid. Note that we need to handle the case where i+a[i]+1 is greater than n separately.
By considering these two possibilities for each position i in the array, we can fill up the dp[] array and find the minimum number of operations required to make the entire array a valid sequence. The final answer will be dp[1] (1-based indexing is considered here) , which represents the minimum number of operations required to make the segment from 1 to n valid.
Steps-by-step approach:
- Initialize the dynamic programming arr with large values
- Helper function to safely access the dp array
- Base case: the last element can always be made valid by removing it by dp[n – 1] = 1
-
Fill the dp array from right to left
- Either remove the current element or skip to the end of the block by dp[i] = min(dp[i + 1] + 1, getDP(i + arr[i] + 1))
- The minimum number of operations to make the whole arr valid is dp[0]
Below is the implementation of the above approach:
#include <bits/stdc++.h> #include <vector> using namespace std;
// Function to solve the problem for a single test case void solve( int & n, vector< int >& arr)
{ // Initialize the dynamic programming arr with large
// values
vector< int > dp(n + 1, n + 1);
// Helper function to safely access the dp array
auto getDP = [&]( int pos) {
if (pos > n) {
return n + 1;
}
if (pos == n) {
return 0;
}
return dp[pos];
};
// Base case: the last element can always be made valid
// by removing it
dp[n - 1] = 1;
// Fill the dp arr from right to left
for ( int i = n - 2; i >= 0; --i) {
// Either remove the current element or skip to the
// end of the block
dp[i] = min(dp[i + 1] + 1, getDP(i + arr[i] + 1));
}
// The minimum number of operations to make the whole
// array valid is dp[0]
cout << dp[0] << endl;
} int main()
{ // number of elements in the arr
int n = 4;
vector< int > arr = { 5, 6, 3, 2 };
solve(n, arr);
return 0;
} |
import java.util.Arrays;
public class Main {
// Function to solve the problem for a single test case
private static void solve( int n, int [] arr)
{
// Initialize the dynamic programming array with
// large values
int [] dp = new int [n + 1 ];
Arrays.fill(dp, n + 1 );
// Helper function to safely access the dp array
// Base case: the last element can always be made
// valid by removing it
dp[n - 1 ] = 1 ;
// Fill the dp array from right to left
for ( int i = n - 2 ; i >= 0 ; --i) {
// Either remove the current element or skip to
// the end of the block
dp[i] = Math.min(dp[i + 1 ] + 1 ,
(i + arr[i] + 1 <= n)
? dp[i + arr[i] + 1 ]
: n + 1 );
}
// The minimum number of operations to make the
// whole array valid is dp[0]
System.out.println(dp[ 0 ]);
}
public static void main(String[] args)
{
// Number of elements in the array
int n = 4 ;
int [] arr = { 5 , 6 , 3 , 2 };
solve(n, arr);
}
} |
# Python program for the above approach # Function to solve the problem for a single test case def solve(n, arr):
# Initialize the dynamic programming array with large values
dp = [n + 1 ] * (n + 1 )
# Base case: the last element can always be made valid by removing it
dp[n - 1 ] = 1
# Helper function to safely access the dp array
def getDP(pos):
if pos > n:
return n + 1
if pos = = n:
return 0
return dp[pos]
# Fill the dp array from right to left
for i in range (n - 2 , - 1 , - 1 ):
# Either remove the current element or skip to the end of the block
dp[i] = min (dp[i + 1 ] + 1 , getDP(i + arr[i] + 1 ))
# The minimum number of operations to make the whole array valid is dp[0]
print (dp[ 0 ])
# Main function if __name__ = = "__main__" :
# Number of elements in the array
n = 4
arr = [ 5 , 6 , 3 , 2 ]
solve(n, arr)
# This code is contributed by Susobhan Akhuli |
using System;
using System.Collections.Generic;
class Program
{ // Function to solve the problem for a single test case
static void Solve( ref int n, List< int > arr)
{
// Initialize the dynamic programming array with large values
List< int > dp = new List< int >( new int [n + 1]);
// Create a local variable to store the value of 'n'
int localN = n;
// Helper function to safely access the dp array
Func< int , int > getDP = ( int pos) =>
{
if (pos > localN)
return localN + 1;
if (pos == localN)
return 0;
return dp[pos];
};
// Base case: the last element can always be made valid by removing it
dp[localN - 1] = 1;
// Fill the dp array from right to left
for ( int i = localN - 2; i >= 0; --i)
{
// Either remove the current element or skip to the end of the block
dp[i] = Math.Min(dp[i + 1] + 1, getDP(i + arr[i] + 1));
}
// The minimum number of operations to make the whole array valid is dp[0]
Console.WriteLine(dp[0]);
}
static void Main()
{
// Number of elements in the array
int n = 4;
List< int > arr = new List< int > { 5, 6, 3, 2 };
Solve( ref n, arr);
}
} |
// Javascript program for the above approach // Function to solve the problem for a single test case function solve(n, arr) {
// Initialize the dynamic programming array with large values
let dp = new Array(n + 1).fill(n + 1);
// Base case: the last element can always be made valid by removing it
dp[n - 1] = 1;
// Fill the dp array from right to left
for (let i = n - 2; i >= 0; --i) {
// Either remove the current element or skip to the end of the block
dp[i] = Math.min(dp[i + 1] + 1, (i + arr[i] + 1 <= n) ? dp[i + arr[i] + 1] : n + 1);
}
// The minimum number of operations to make the whole array valid is dp[0]
console.log(dp[0]);
} // Number of elements in the array let n = 4; // Array of elements let arr = [5, 6, 3, 2]; // Solve the problem for the given test case solve(n, arr); // This code is contributed by Susobhan Akhuli |
4
Time Complexity: O(n), where n is the size of the array.
Auxiliary Space: O(n)