Given an array arr, find the minimum operations required to make it non-increasing. You can select any one of the following operations and preform it any number of times(including zero) on an array element.
- Increment the array element by 1.
- Decrement the array element by 1.
Examples:
Input : arr[] = [3, 1, 2, 1]
Output : 1
Explanation : We can convert the array into [3, 1, 1, 1] by changing 3rd element of array i.e. 2 into its previous integer 1 in one step. Hence, only one step is required.Input : arr[] = [3, 1, 5, 1]
Output : 4
Explanation : We need to decrease 5 to 1 to make array sorted in non-increasing order. The final non-increasing array is [3, 1, 1, 1].Input : arr[] = [5, 5, 5, 5]
Output : 0
Explanation: The array is already non-increasing.
Table of Content
[Naive Approach] Using Recursion - O((maxElement) ^ n) Time and O(n) Space
We recursively try all possible target values for each element (ranging from 0 to the previous element’s value) and compute the cost of converting the current element to that value. Among all such configurations, we take the minimum cost. Since we explore all combinations, the time complexity is exponential.
Step By Step Implementation:
- Find the maximum of the array and start recursion from index 0 with prev = maxVal.
- At each index, try all possible values from 0 to prev so that the array remains non-increasing.
- Calculate the cost to convert the current element into the chosen value using: abs(arr[idx]−val).
- Recursively solve the remaining array for the next index using the current chosen value as the new prev.
- Store the minimum cost obtained among all possible choices for the current index.
- When all elements are processed (idx == n), return 0, and finally return the minimum operations obtained from recursion.
#include <bits/stdc++.h>
using namespace std;
int solve(int idx, int prev, vector<int> &arr)
{
// Base Case
if (idx == arr.size())
return 0;
int ans = INT_MAX;
// Current value can be anything from 0 to prev
for (int val = 0; val <= prev; val++)
{
// Cost to convert arr[idx] -> val
int cost = abs(arr[idx] - val);
// Recursive call
cost += solve(idx + 1, val, arr);
ans = min(ans, cost);
}
return ans;
}
int minOperations(vector<int> &arr)
{
// Maximum value in array
int maxVal = *max_element(arr.begin(), arr.end());
// Start recursion
return solve(0, maxVal, arr);
}
int main()
{
vector<int> arr = {3, 1, 2, 1};
cout << minOperations(arr);
return 0;
}
import java.util.*;
public class GFG {
static int solve(int idx, int prev, int[] arr)
{
// Base Case
if (idx == arr.length)
return 0;
int ans = Integer.MAX_VALUE;
// Try all possible values from 0 to prev
for (int val = 0; val <= prev; val++) {
// Cost to convert arr[idx] -> val
int cost = Math.abs(arr[idx] - val);
// Recursive call
cost += solve(idx + 1, val, arr);
ans = Math.min(ans, cost);
}
return ans;
}
static int minOperations(int[] arr)
{
// Find maximum element
int maxVal = Arrays.stream(arr).max().getAsInt();
// Start recursion
return solve(0, maxVal, arr);
}
public static void main(String[] args)
{
int[] arr = { 3, 1, 2, 1 };
System.out.println(minOperations(arr));
}
}
def solve(idx, prev, arr):
# Base Case
if idx == len(arr):
return 0
ans = float('inf')
# Try all possible values from 0 to prev
for val in range(prev + 1):
# Cost to convert arr[idx] -> val
cost = abs(arr[idx] - val)
# Recursive call
cost += solve(idx + 1, val, arr)
ans = min(ans, cost)
return ans
def minOperations(arr):
# Find maximum element
maxVal = max(arr)
# Start recursion
return solve(0, maxVal, arr)
# Driver Code
if __name__ == "__main__":
arr = [3, 1, 2, 1]
print(minOperations(arr))
using System;
class GFG {
static int Solve(int idx, int prev, int[] arr)
{
// Base Case
if (idx == arr.Length)
return 0;
int ans = int.MaxValue;
// Try all possible values from 0 to prev
for (int val = 0; val <= prev; val++) {
// Cost to convert arr[idx] -> val
int cost = Math.Abs(arr[idx] - val);
// Recursive call
cost += Solve(idx + 1, val, arr);
ans = Math.Min(ans, cost);
}
return ans;
}
static int minOperations(int[] arr)
{
// Find maximum element
int maxVal = arr[0];
foreach(int num in arr)
{
maxVal = Math.Max(maxVal, num);
}
// Start recursion
return Solve(0, maxVal, arr);
}
static void Main()
{
int[] arr = { 3, 1, 2, 1 };
Console.WriteLine(minOperations(arr));
}
}
function solve(idx, prev, arr)
{
// Base Case
if (idx === arr.length)
return 0;
let ans = Number.MAX_VALUE;
// Try all possible values from 0 to prev
for (let val = 0; val <= prev; val++) {
// Cost to convert arr[idx] -> val
let cost = Math.abs(arr[idx] - val);
// Recursive call
cost += solve(idx + 1, val, arr);
ans = Math.min(ans, cost);
}
return ans;
}
function minOperations(arr)
{
// Find maximum element
let maxVal = Math.max(...arr);
// Start recursion
return solve(0, maxVal, arr);
}
// Driver Code
let arr = [ 3, 1, 2, 1 ];
console.log(minOperations(arr));
Output
1
[Better Approach] Using Dynamic Programming - O((maxElement ^ 2) * n) Time and O(maxElement * n) Space
The recursive solution tries every possible value for each element, which creates many overlapping states that get recomputed multiple times. To avoid this, we use Bottom-Up DP, where we iteratively build answers for smaller subproblems and store them in a DP table. For each element, we try all possible current values and combine them with valid previous values to maintain the non-increasing order while minimizing the total cost.
Step By Step Implementation:
- Find the maximum element maxVal in the array.
- Create a DP table dp[i][j], where dp[i][j] stores the minimum operations needed to make the first i + 1 elements non-increasing if arr[i] is converted to value j.
- Initialize the first row of the DP table by converting the first element into every possible value from 0 to maxVal.
- Traverse the array from left to right starting from index 1.
- For every current value curr, try all possible previous values prev such that prev >= curr to maintain the non-increasing order.
- Update the DP state using: dp[i][curr] = min(dp[i][curr], dp[i − 1][prev] + abs(arr[i] − curr))
- The minimum value in the last row of the DP table gives the minimum operations required to make the entire array non-increasing.
#include <algorithm>
#include <climits>
#include <iostream>
#include <vector>
using namespace std;
// Bottom-Up DP Approach dp[i][j] = Minimum operations required to make
// first i elements non-increasing, where current element becomes j.
int minOperations(vector<int> &arr)
{
int n = arr.size();
// Maximum element in array
int maxVal = *max_element(arr.begin(), arr.end());
vector<vector<int>> dp(n, vector<int>(maxVal + 1, INT_MAX));
// Base Case for first element
// We can convert arr[0] to any value j
for (int j = 0; j <= maxVal; j++)
{
dp[0][j] = abs(arr[0] - j);
}
for (int i = 1; i < n; i++)
{
for (int curr = 0; curr <= maxVal; curr++)
{
// Previous value must be >= current value
// to maintain non-increasing order
for (int prev = curr; prev <= maxVal; prev++)
{
if (dp[i - 1][prev] != INT_MAX)
{
dp[i][curr] = min(dp[i][curr], dp[i - 1][prev] + abs(arr[i] - curr));
}
}
}
}
// Find minimum value in last row
int ans = INT_MAX;
for (int j = 0; j <= maxVal; j++)
{
ans = min(ans, dp[n - 1][j]);
}
return ans;
}
int main()
{
vector<int> arr = {3, 1, 2, 1};
cout << minOperations(arr);
return 0;
}
import java.util.*;
class GFG {
static int minOperations(int[] arr)
{
int n = arr.length;
// Find maximum element
int maxVal = Arrays.stream(arr).max().getAsInt();
int[][] dp = new int[n][maxVal + 1];
// Initialize with large value
for (int i = 0; i < n; i++) {
Arrays.fill(dp[i], Integer.MAX_VALUE);
}
// Base Case
for (int j = 0; j <= maxVal; j++) {
dp[0][j] = Math.abs(arr[0] - j);
}
for (int i = 1; i < n; i++) {
for (int curr = 0; curr <= maxVal; curr++) {
// Previous value must be >= current value
for (int prev = curr; prev <= maxVal;
prev++) {
if (dp[i - 1][prev]
!= Integer.MAX_VALUE) {
dp[i][curr] = Math.min(dp[i][curr], dp[i - 1][prev] + Math.abs(arr[i] - curr));
}
}
}
}
// Find minimum answer
int ans = Integer.MAX_VALUE;
for (int j = 0; j <= maxVal; j++) {
ans = Math.min(ans, dp[n - 1][j]);
}
return ans;
}
public static void main(String[] args)
{
int[] arr = { 3, 1, 2, 1 };
System.out.println(minOperations(arr));
}
}
import math
def minOperations(arr):
n = len(arr)
# Find maximum element
maxVal = max(arr)
dp = [[math.inf] * (maxVal + 1) for _ in range(n)]
# Base Case
for j in range(maxVal + 1):
dp[0][j] = abs(arr[0] - j)
# Fill DP Table
for i in range(1, n):
for curr in range(maxVal + 1):
# Previous value must be >= current value
for prev in range(curr, maxVal + 1):
if dp[i - 1][prev] != math.inf:
dp[i][curr] = min(
dp[i][curr],
dp[i - 1][prev] + abs(arr[i] - curr)
)
# Find minimum answer
ans = math.inf
for j in range(maxVal + 1):
ans = min(ans, dp[n - 1][j])
return ans
# Driver Code
if __name__ == "__main__":
arr = [3, 1, 2, 1]
print(minOperations(arr))
using System;
class GFG {
static int minOperations(int[] arr)
{
int n = arr.Length;
// Find maximum element
int maxVal = arr[0];
foreach(int num in arr)
{
maxVal = Math.Max(maxVal, num);
}
int[, ] dp = new int[n, maxVal + 1];
// Initialize DP array
for (int i = 0; i < n; i++) {
for (int j = 0; j <= maxVal; j++) {
dp[i, j] = int.MaxValue;
}
}
// Base Case
for (int j = 0; j <= maxVal; j++) {
dp[0, j] = Math.Abs(arr[0] - j);
}
// Fill DP Table
for (int i = 1; i < n; i++) {
for (int curr = 0; curr <= maxVal; curr++) {
// Previous value must be >= current value
for (int prev = curr; prev <= maxVal;
prev++) {
if (dp[i - 1, prev] != int.MaxValue) {
dp[i, curr] = Math.Min(
dp[i, curr],
dp[i - 1, prev]
+ Math.Abs(arr[i] - curr));
}
}
}
}
// Find minimum answer
int ans = int.MaxValue;
for (int j = 0; j <= maxVal; j++) {
ans = Math.Min(ans, dp[n - 1, j]);
}
return ans;
}
static void Main()
{
int[] arr = { 3, 1, 2, 1 };
Console.WriteLine(minOperations(arr));
}
}
function minOperations(arr)
{
let n = arr.length;
// Find maximum element
let maxVal = Math.max(...arr);
let dp = Array.from(
{length : n},
() => Array(maxVal + 1).fill(Number.MAX_VALUE));
// Base Case
for (let j = 0; j <= maxVal; j++) {
dp[0][j] = Math.abs(arr[0] - j);
}
// Fill DP Table
for (let i = 1; i < n; i++) {
for (let curr = 0; curr <= maxVal; curr++) {
// Previous value must be >= current value
for (let prev = curr; prev <= maxVal; prev++) {
if (dp[i - 1][prev] !== Number.MAX_VALUE) {
dp[i][curr] = Math.min(
dp[i][curr],
dp[i - 1][prev]
+ Math.abs(arr[i] - curr));
}
}
}
}
// Find minimum answer
let ans = Number.MAX_VALUE;
for (let j = 0; j <= maxVal; j++) {
ans = Math.min(ans, dp[n - 1][j]);
}
return ans;
}
// Driver Code
let arr = [ 3, 1, 2, 1 ];
console.log(minOperations(arr));
Output
1
[Expected Approach] Using Max-Heap - O(n log n) Time and O(n) Space
First reverse the array so that making it non-increasing becomes making it non-decreasing. While traversing the reversed array, maintain the chosen values in a max-heap. If the largest chosen value is greater than the current element, it violates the non-decreasing order. We reduce that value to the current element and add the difference to the answer. This greedy adjustment ensures the minimum total cost.
Why does this approach work?
- After reversing, we need: arr[0] <= arr[1] <= arr[2] <= ... <= arr[n-1].
- When processing an element
x, all previously chosen values should be at mostx. - If the maximum previous value is greater than
x, at least one violation exists. - The cheapest way to remove that violation is to reduce the largest offending value directly to
x. - Any optimal solution must pay at least this reduction cost, so performing it immediately is safe.
- The max-heap helps identify this largest violating value in
O(log n)time.
Step By Step Implementation:
- Reverse the array.
- Initialize a max-heap and a variable cost = 0.
- Traverse the reversed array from left to right.
- Insert the current element into the max-heap.
- If the largest value in the heap is greater than the current element: Add the difference to cost, remove the largest value from the heap and insert the current element instead.
- After processing all elements, return cost.
#include <iostream>
#include <queue>
#include <vector>
#include <algorithm>
using namespace std;
int minOperations(vector<int>& arr) {
reverse(arr.begin(), arr.end());
priority_queue<int> pq;
int cost = 0;
for (int x : arr) {
pq.push(x);
// Reduce the largest value if it exceeds x.
if (pq.top() > x) {
cost += pq.top() - x;
pq.pop();
pq.push(x);
}
}
return cost;
}
int main() {
vector<int> arr = {3, 1, 2, 1};
cout << minOperations(arr);
return 0;
}
import java.util.Collections;
import java.util.PriorityQueue;
class GFG {
static int minOperations(int[] arr) {
int n = arr.length;
for (int i = 0; i < n / 2; i++) {
int temp = arr[i];
arr[i] = arr[n - 1 - i];
arr[n - 1 - i] = temp;
}
PriorityQueue<Integer> pq =
new PriorityQueue<>(Collections.reverseOrder());
int cost = 0;
for (int x : arr) {
pq.offer(x);
// Reduce the largest value if it exceeds x.
if (pq.peek() > x) {
cost += pq.peek() - x;
pq.poll();
pq.offer(x);
}
}
return cost;
}
public static void main(String[] args) {
int[] arr = {3, 1, 2, 1};
System.out.println(minOperations(arr));
}
}
import heapq
def minOperations(arr):
arr.reverse()
heap = []
cost = 0
for x in arr:
heapq.heappush(heap, -x)
# Reduce the largest value if it exceeds x.
if -heap[0] > x:
cost += (-heap[0]) - x
heapq.heappop(heap)
heapq.heappush(heap, -x)
return cost
arr = [3, 1, 2, 1]
print(minOperations(arr))
using System;
using System.Collections.Generic;
class GFG
{
static int MinOperations(int[] arr)
{
Array.Reverse(arr);
PriorityQueue<int, int> pq = new();
int cost = 0;
foreach (int x in arr)
{
pq.Enqueue(x, -x);
// Reduce the largest value if it exceeds x.
if (pq.Peek() > x)
{
cost += pq.Peek() - x;
pq.Dequeue();
pq.Enqueue(x, -x);
}
}
return cost;
}
static void Main()
{
int[] arr = { 3, 1, 2, 1 };
Console.WriteLine(MinOperations(arr));
}
}
class MaxHeap {
constructor() {
this.heap = [];
}
push(x) {
this.heap.push(x);
let i = this.heap.length - 1;
while (i > 0) {
let p = Math.floor((i - 1) / 2);
if (this.heap[p] >= this.heap[i]) break;
[this.heap[p], this.heap[i]] =
[this.heap[i], this.heap[p]];
i = p;
}
}
pop() {
const top = this.heap[0];
const last = this.heap.pop();
if (this.heap.length) {
this.heap[0] = last;
let i = 0;
while (true) {
let l = 2 * i + 1;
let r = 2 * i + 2;
let largest = i;
if (l < this.heap.length &&
this.heap[l] > this.heap[largest]) {
largest = l;
}
if (r < this.heap.length &&
this.heap[r] > this.heap[largest]) {
largest = r;
}
if (largest === i) break;
[this.heap[i], this.heap[largest]] =
[this.heap[largest], this.heap[i]];
i = largest;
}
}
return top;
}
top() {
return this.heap[0];
}
}
function minOperations(arr) {
arr.reverse();
const pq = new MaxHeap();
let cost = 0;
for (const x of arr) {
pq.push(x);
// Reduce the largest value if it exceeds x.
if (pq.top() > x) {
cost += pq.top() - x;
pq.pop();
pq.push(x);
}
}
return cost;
}
// Driver Code
const arr = [3, 1, 2, 1];
console.log(minOperations(arr));
Output
1