Minimum number of increasing subsequences
Given an array of integers of size N, you have to divide it into the minimum number of “strictly increasing subsequences”
For example: let the sequence be {1, 3, 2, 4}, then the answer would be 2. In this case, the first increasing sequence would be {1, 3, 4} and the second would be {2}.
Examples:
Input : arr[] = {1, 3, 2, 4}
Output: 2
There are two increasing subsequences {1, 3, 4} and {2}Input : arr[] = {4, 3, 2, 1}
Output : 4Input : arr[] = {1, 2, 3, 4}
Output : 1Input : arr[] = {1, 6, 2, 4, 3}
Output : 3
Approach: To solve the problem, follow the below idea:
If we focus on the examples, we can see that the Minimum number of increasing subsequences equals to the length of longest decreasing subsequence where each element from the longest decreasing subsequence belongs to a single increasing subsequence, so it can be found in N*Log(N) time complexity in the same way as longest increasing subsequence by multiplying all the elements with -1.
Step-by-step approach:
- Initialize a dp[] array of length N.
- Multiply all the elements of the array with -1, so that our problem now gets reduced to finding the length of longest increasing subsequence.
- Return the length of longest increasing subsequence using Binary Search.
Below is the implementation of above approach:
// C++ program for the above approach
#include <bits/stdc++.h>
using namespace std;
// To search for correct position of num in array dp
int search(vector<int> dp, int num)
{
// Initialise low,high and ans
int low = 0, high = dp.size() - 1;
int ans = -1;
while (low <= high) {
// Get mid
int mid = low + ((high - low) / 2);
// If mid element is >=num search for left half
if (dp[mid] >= num) {
ans = mid;
high = mid - 1;
}
else
low = mid + 1;
}
return ans;
}
int longestIncreasingSubsequence(vector<int> arr, int N)
{
vector<int> ans;
// Initialize the answer vector with the
// first element of nums
ans.push_back(arr[0]);
for (int i = 1; i < N; i++) {
if (arr[i] > ans.back()) {
// If the current number is greater
// than the last element of the answer
// vector, it means we have found a
// longer increasing subsequence.
// Hence, we append the current number
// to the answer vector.
ans.push_back(arr[i]);
}
else {
// If the current number is not
// greater than the last element of
// the answer vector, we perform
// a binary search to find the smallest
// element in the answer vector that
// is greater than or equal to the
// current number.
// The lower_bound function returns
// an iterator pointing to the first
// element that is not less than
// the current number.
int low = lower_bound(ans.begin(), ans.end(),
arr[i])
- ans.begin();
// We update the element at the
// found position with the current number.
// By doing this, we are maintaining
// a sorted order in the answer vector.
ans[low] = arr[i];
}
}
// The length of the answer vector
// represents the length of the
// longest increasing subsequence.
return ans.size();
}
// Driver code
int main()
{
int N = 5;
vector<int> arr{1, 6, 2, 4, 3};
for (int i = 0; i < N; i++)
arr[i] = -arr[i];
cout << longestIncreasingSubsequence(arr, N) << endl;
return 0;
}
// This code is contributed by shinjanpatra
import java.util.*;
class Main {
// To search for correct position of num in array dp
static int search(ArrayList<Integer> dp, int num) {
// Initialise low, high and ans
int low = 0, high = dp.size() - 1;
int ans = -1;
while (low <= high) {
// Get mid
int mid = low + ((high - low) / 2);
// If mid element is >=num search for left half
if (dp.get(mid) >= num) {
ans = mid;
high = mid - 1;
} else {
low = mid + 1;
}
}
return ans;
}
static int longestIncreasingSubsequence(ArrayList<Integer> arr, int N) {
ArrayList<Integer> ans = new ArrayList<>();
// Initialize the answer ArrayList with the first element of arr
ans.add(arr.get(0));
for (int i = 1; i < N; i++) {
if (arr.get(i) > ans.get(ans.size() - 1)) {
// If the current number is greater than the last element of the answer ArrayList,
// it means we have found a longer increasing subsequence.
// Hence, we append the current number to the answer ArrayList.
ans.add(arr.get(i));
} else {
// If the current number is not greater than the last element of the answer ArrayList,
// we perform a binary search to find the smallest element in the answer ArrayList
// that is greater than or equal to the current number.
int low = Collections.binarySearch(ans, arr.get(i));
if (low < 0) {
low = -(low + 1);
}
// We update the element at the found position with the current number.
// By doing this, we are maintaining a sorted order in the answer ArrayList.
ans.set(low, arr.get(i));
}
}
// The size of the answer ArrayList represents the length of the longest increasing subsequence.
return ans.size();
}
// Driver code
public static void main(String[] args) {
int N = 5;
ArrayList<Integer> arr = new ArrayList<>(Arrays.asList(1, 6, 2, 4, 3));
for (int i = 0; i < N; i++) {
arr.set(i, -arr.get(i));
}
System.out.println(longestIncreasingSubsequence(arr, N));
}
}
using System;
using System.Collections.Generic;
public class LIS
{
// To search for the correct position of num in the array dp
static int Search(List<int> dp, int num)
{
// Initialize low, high, and ans
int low = 0, high = dp.Count - 1;
int ans = -1;
while (low <= high)
{
// Get mid
int mid = low + ((high - low) / 2);
// If mid element is >= num search for left half
if (dp[mid] >= num)
{
ans = mid;
high = mid - 1;
}
else
low = mid + 1;
}
return ans;
}
static int LongestIncreasingSubsequence(List<int> arr, int N)
{
List<int> ans = new List<int>();
// Initialize the answer list with the first element of nums
ans.Add(arr[0]);
for (int i = 1; i < N; i++)
{
if (arr[i] > ans[ans.Count - 1])
{
// If the current number is greater than the last element of the answer
// list, it means we have found a longer increasing subsequence.
// Hence, we append the current number to the answer list.
ans.Add(arr[i]);
}
else
{
// If the current number is not greater than the last element of
// the answer list, we perform a binary search to find the smallest
// element in the answer list that is greater than or equal to the
// current number.
// The BinarySearch method returns the index of the first element that
// is not less than the current number.
int index = ans.BinarySearch(arr[i]);
if (index < 0)
index = ~index; // If not found, BinarySearch returns bitwise complement of the index to insert
// We update the element at the found position with the current number.
// By doing this, we are maintaining a sorted order in the answer list.
ans[index] = arr[i];
}
}
// The length of the answer list represents the length of the
// longest increasing subsequence.
return ans.Count;
}
// Driver code
public static void Main(string[] args)
{
int N = 5;
List<int> arr = new List<int> { 1, 6, 2, 4, 3 };
for (int i = 0; i < N; i++)
arr[i] = -arr[i]; // Negating the array elements to find the longest decreasing subsequence
Console.WriteLine(LongestIncreasingSubsequence(arr, N));
}
}
// Function to search for the correct position of num in the array dp using binary search
function search(dp, num) {
let low = 0, high = dp.length - 1;
let ans = -1;
while (low <= high) {
let mid = low + Math.floor((high - low) / 2);
if (dp[mid] >= num) {
// If mid element is >= num, search for the left half
ans = mid;
high = mid - 1;
} else {
// If mid element is < num, search for the right half
low = mid + 1;
}
}
return ans;
}
// Function to find the length of the longest increasing subsequence
function longestIncreasingSubsequence(arr, N) {
let ans = [];
// Initialize the answer array with the first element of arr
ans.push(arr[0]);
for (let i = 1; i < N; i++) {
if (arr[i] > ans[ans.length - 1]) {
// If the current number is greater than the last element of the answer array,
// it means we have found a longer increasing subsequence.
// Hence, we append the current number to the answer array.
ans.push(arr[i]);
} else {
// If the current number is not greater than the last element of the answer array,
// perform a binary search to find the smallest element in the answer array
// that is greater than or equal to the current number.
let low = ans.findIndex(el => el >= arr[i]);
if (low < 0) {
low = -(low + 1);
}
// Update the element at the found position with the current number.
// By doing this, we are maintaining a sorted order in the answer array.
ans[low] = arr[i];
}
}
// The length of the answer array represents the length of the longest increasing subsequence.
return ans.length;
}
// Driver code
const N = 5;
let arr = [1, 6, 2, 4, 3];
arr = arr.map(num => -num); // Negating elements of the array
console.log(longestIncreasingSubsequence(arr, N));
import bisect
def longestIncreasingSubsequence(arr):
ans = []
# Initialize the answer list with the
# first element of arr
ans.append(arr[0])
# Traverse through the array starting from
# the second element
for i in range(1, len(arr)):
if arr[i] > ans[-1]:
# If the current number is greater
# than the last element of the answer
# list, it means we have found a
# longer increasing subsequence.
# Hence, we append the current number
# to the answer list.
ans.append(arr[i])
else:
# If the current number is not
# greater than the last element of
# the answer list, we perform
# a binary search to find the smallest
# element in the answer list that
# is greater than or equal to the
# current number.
# The bisect.bisect_left() function
# returns the index where the current
# number should be inserted in order
# to maintain the sorted order.
low = bisect.bisect_left(ans, arr[i])
# We update the element at the
# found position with the current number.
# By doing this, we are maintaining
# a sorted order in the answer list.
ans[low] = arr[i]
# The length of the answer list
# represents the length of the
# longest increasing subsequence.
return len(ans)
# Driver code
if __name__ == "__main__":
arr = [-1, -6, -2, -4, -3]
print(longestIncreasingSubsequence(arr))
Output
3
Time complexity: O(N * log(N))
Auxiliary Space: O(N)