Find Minimum Removals for Valid Block Sequences
Last Updated :
16 Feb, 2024
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:
C++
#include <bits/stdc++.h>
#include <vector>
using namespace std;
void solve( int & n, vector< int >& arr)
{
vector< int > dp(n + 1, n + 1);
auto getDP = [&]( int pos) {
if (pos > n) {
return n + 1;
}
if (pos == n) {
return 0;
}
return dp[pos];
};
dp[n - 1] = 1;
for ( int i = n - 2; i >= 0; --i) {
dp[i] = min(dp[i + 1] + 1, getDP(i + arr[i] + 1));
}
cout << dp[0] << endl;
}
int main()
{
int n = 4;
vector< int > arr = { 5, 6, 3, 2 };
solve(n, arr);
return 0;
}
|
Java
import java.util.Arrays;
public class Main {
private static void solve( int n, int [] arr)
{
int [] dp = new int [n + 1 ];
Arrays.fill(dp, n + 1 );
dp[n - 1 ] = 1 ;
for ( int i = n - 2 ; i >= 0 ; --i) {
dp[i] = Math.min(dp[i + 1 ] + 1 ,
(i + arr[i] + 1 <= n)
? dp[i + arr[i] + 1 ]
: n + 1 );
}
System.out.println(dp[ 0 ]);
}
public static void main(String[] args)
{
int n = 4 ;
int [] arr = { 5 , 6 , 3 , 2 };
solve(n, arr);
}
}
|
Python3
def solve(n, arr):
dp = [n + 1 ] * (n + 1 )
dp[n - 1 ] = 1
def getDP(pos):
if pos > n:
return n + 1
if pos = = n:
return 0
return dp[pos]
for i in range (n - 2 , - 1 , - 1 ):
dp[i] = min (dp[i + 1 ] + 1 , getDP(i + arr[i] + 1 ))
print (dp[ 0 ])
if __name__ = = "__main__" :
n = 4
arr = [ 5 , 6 , 3 , 2 ]
solve(n, arr)
|
C#
using System;
using System.Collections.Generic;
class Program
{
static void Solve( ref int n, List< int > arr)
{
List< int > dp = new List< int >( new int [n + 1]);
int localN = n;
Func< int , int > getDP = ( int pos) =>
{
if (pos > localN)
return localN + 1;
if (pos == localN)
return 0;
return dp[pos];
};
dp[localN - 1] = 1;
for ( int i = localN - 2; i >= 0; --i)
{
dp[i] = Math.Min(dp[i + 1] + 1, getDP(i + arr[i] + 1));
}
Console.WriteLine(dp[0]);
}
static void Main()
{
int n = 4;
List< int > arr = new List< int > { 5, 6, 3, 2 };
Solve( ref n, arr);
}
}
|
Javascript
function solve(n, arr) {
let dp = new Array(n + 1).fill(n + 1);
dp[n - 1] = 1;
for (let i = n - 2; i >= 0; --i) {
dp[i] = Math.min(dp[i + 1] + 1, (i + arr[i] + 1 <= n) ? dp[i + arr[i] + 1] : n + 1);
}
console.log(dp[0]);
}
let n = 4;
let arr = [5, 6, 3, 2];
solve(n, arr);
|
Time Complexity: O(n), where n is the size of the array.
Auxiliary Space: O(n)
Share your thoughts in the comments
Please Login to comment...