Given an array arr[] of n positive integers and an integer k, perform exactly n operations on the array. In each operation, any element from the array can be selected, and the same element can be selected multiple times.
- For a selected element with value x, decrease its value by the larger of: floor(x / 10) (10% of its current value), or k
- If the value of the selected element is less than k, reduce it to 0.
After performing exactly n operations, return the minimum possible sum of the array elements.
Examples:
Input: arr[] = [100, 15], k = 10
Output: 95
Explanation: Reduce 100 -> 90 -> 80 (both times by 10). Sum = 80 + 15 = 95.Input: arr[] = [90, 100], k = 10
Output: 170
Explanation: Reduce 100 -> 90, then reduce one 90 -> 80. Sum = 80 + 90 = 170.
Table of Content
[Naive Approach] Simulate All Possible Operations - O(n ^ n * n) Time and O(n) Space
The idea is to try every possible choice of element in each of the n operations. For every operation, recursively select an element, apply the reduction, and continue until all operations are completed. Finally, return the minimum sum among all possible sequences of operations.
Working of Approach:
- Perform recursion for exactly n operations.
- In each operation, try reducing every array element.
- Recur for the next operation after updating the selected element.
- Backtrack to restore the original value.
- Return the minimum sum obtained.
#include <bits/stdc++.h>
using namespace std;
// Recursive function to find the minimum possible array sum
// after performing the remaining operations.
int findMinSum(vector<int> &arr, int k, int operationsDone)
{
int n = arr.size();
// If all operations are completed, return the current array sum.
if (operationsDone == n)
{
int sum = 0;
for (int x : arr)
sum += x;
return sum;
}
int res = INT_MAX;
// Try selecting every array element for the current operation.
for (int i = 0; i < n; i++)
{
int originalValue = arr[i];
// Reduce the selected element according to the given rule.
if (arr[i] <= k)
arr[i] = 0;
else
arr[i] -= max(arr[i] / 10, k);
// Recur for the next operation.
res = min(res, findMinSum(arr, k, operationsDone + 1));
// Restore the original value (backtracking).
arr[i] = originalValue;
}
return res;
}
int minSum(vector<int> &arr, int k)
{
return findMinSum(arr, k, 0);
}
int main()
{
vector<int> arr = {90, 100};
int k = 10;
cout << minSum(arr, k);
return 0;
}
import java.util.Arrays;
public class GFG {
// Recursive function to find the minimum possible array
// sum after performing the remaining operations.
static int findMinSum(int[] arr, int k,
int operationsDone)
{
int n = arr.length;
// If all operations are completed, return the
// current array sum.
if (operationsDone == n) {
int sum = 0;
for (int x : arr)
sum += x;
return sum;
}
int res = Integer.MAX_VALUE;
// Try selecting every array element for the current
// operation.
for (int i = 0; i < n; i++) {
int originalValue = arr[i];
// Reduce the selected element according to the
// given rule.
if (arr[i] <= k)
arr[i] = 0;
else
arr[i] -= Math.max(arr[i] / 10, k);
// Recur for the next operation.
res = Math.min(
res,
findMinSum(arr, k, operationsDone + 1));
// Restore the original value (backtracking).
arr[i] = originalValue;
}
return res;
}
static int minSum(int[] arr, int k)
{
return findMinSum(arr, k, 0);
}
public static void main(String[] args)
{
int[] arr = { 90, 100 };
int k = 10;
System.out.println(minSum(arr, k));
}
}
def findMinSum(arr, k, operationsDone):
n = len(arr)
# If all operations are completed, return the current array sum.
if operationsDone == n:
return sum(arr)
res = float('inf')
# Try selecting every array element for the current operation.
for i in range(n):
originalValue = arr[i]
# Reduce the selected element according to the given rule.
if arr[i] <= k:
arr[i] = 0
else:
arr[i] -= max(arr[i] // 10, k)
# Recur for the next operation.
res = min(res, findMinSum(arr, k, operationsDone + 1))
# Restore the original value (backtracking).
arr[i] = originalValue
return res
def minSum(arr, k):
return findMinSum(arr, k, 0)
# Driver Code
if __name__ == "__main__":
arr = [90, 100]
k = 10
print(minSum(arr, k))
using System;
public class GFG {
// Recursive function to find the minimum possible array
// sum after performing the remaining operations.
static int FindMinSum(int[] arr, int k,
int operationsDone)
{
int n = arr.Length;
// If all operations are completed, return the
// current array sum.
if (operationsDone == n) {
int sum = 0;
foreach(int x in arr) sum += x;
return sum;
}
int res = int.MaxValue;
// Try selecting every array element for the current
// operation.
for (int i = 0; i < n; i++) {
int originalValue = arr[i];
// Reduce the selected element according to the
// given rule.
if (arr[i] <= k)
arr[i] = 0;
else
arr[i] -= Math.Max(arr[i] / 10, k);
// Recur for the next operation.
res = Math.Min(
res,
FindMinSum(arr, k, operationsDone + 1));
// Restore the original value (backtracking).
arr[i] = originalValue;
}
return res;
}
static int minSum(int[] arr, int k)
{
return FindMinSum(arr, k, 0);
}
public static void Main()
{
int[] arr = { 90, 100 };
int k = 10;
Console.WriteLine(minSum(arr, k));
}
}
function findMinSum(arr, k, operationsDone)
{
let n = arr.length;
// If all operations are completed, return the current
// array sum.
if (operationsDone === n) {
return arr.reduce((sum, val) => sum + val, 0);
}
let res = Number.MAX_SAFE_INTEGER;
// Try selecting every array element for the current
// operation.
for (let i = 0; i < n; i++) {
let originalValue = arr[i];
// Reduce the selected element according to the
// given rule.
if (arr[i] <= k)
arr[i] = 0;
else
arr[i] -= Math.max(Math.floor(arr[i] / 10), k);
// Recur for the next operation.
res = Math.min(
res, findMinSum(arr, k, operationsDone + 1));
// Restore the original value (backtracking).
arr[i] = originalValue;
}
return res;
}
function minSum(arr, k) { return findMinSum(arr, k, 0); }
// Driver Code
let arr = [ 90, 100 ];
let k = 10;
console.log(minSum(arr, k));
Output
170
[Expected Approach] Greedy using Max Heap - O(n log n) Time and O(n) Space
The idea is to always reduce the largest element of the array. Since the reduction amount increases (or remains the same) with the element value, reducing the maximum element in every operation gives the minimum possible final sum. A max heap helps efficiently retrieve the largest element.
Working of Approach:
- Insert all array elements into a max heap.
- Perform exactly n operations.
- Remove the largest element and reduce it according to the given rule.
- Insert the updated value back if it is greater than 0.
- Sum all remaining elements in the heap.
Let us understand with an example:
Input: arr[] = [90, 100], k = 10
- Insert all elements into a max heap: [100, 90].
- 1st operation: Pick the largest element 100, reduce it by 10 -> 90, and push it back. Heap becomes [90, 90].
- 2nd operation: Pick one of the 90s, reduce it by 10 -> 80, and push it back. Heap becomes [90, 80].
- Sum of the remaining elements = 90 + 80 = 170, which is the minimum possible sum after exactly 2 operations.
#include <bits/stdc++.h>
using namespace std;
int minSum(vector<int> &arr, int k)
{
priority_queue<int> pq;
for (int x : arr)
pq.push(x);
int n = arr.size();
// perform n operations, each time reducing the largest element
for (int i = 0; i < n && !pq.empty(); i++)
{
int cur = pq.top();
pq.pop();
if (cur > k)
{
if (cur < 10 * k)
cur -= k;
else
cur = (9 * cur) / 10;
pq.push(cur);
}
// if cur <= k, it becomes 0 and is discarded (not pushed back)
}
int res = 0;
while (!pq.empty())
{
res += pq.top();
pq.pop();
}
return res;
}
int main()
{
vector<int> arr = {90, 100};
int k = 10;
cout << minSum(arr, k);
return 0;
}
import java.util.*;
public class GFG {
int minSum(int[] arr, int k)
{
PriorityQueue<Integer> pq = new PriorityQueue<>(
Collections.reverseOrder());
for (int x : arr)
pq.add(x);
int n = arr.length;
// perform n operations, each time reducing the
// largest element
for (int i = 0; i < n && !pq.isEmpty(); i++) {
int cur = pq.poll();
if (cur > k) {
if (cur < 10 * k)
cur -= k;
else
cur = (9 * cur) / 10;
pq.add(cur);
}
// if cur <= k, it becomes 0 and is discarded
// (not pushed back)
}
int res = 0;
while (!pq.isEmpty()) {
res += pq.poll();
}
return res;
}
public static void main(String[] args)
{
GFG main = new GFG();
int[] arr = { 90, 100 };
int k = 10;
System.out.println(main.minSum(arr, k));
}
}
import heapq
def minSum(arr, k):
pq = [-x for x in arr]
heapq.heapify(pq)
n = len(arr)
# perform n operations, each time reducing the largest element
for i in range(n):
if pq:
cur = -heapq.heappop(pq)
if cur > k:
if cur < 10 * k:
cur -= k
else:
cur = (9 * cur) // 10
heapq.heappush(pq, -cur)
# if cur <= k, it becomes 0 and is discarded (not pushed back)
res = 0
while pq:
res -= heapq.heappop(pq)
return res
if __name__ == '__main__':
arr = [90, 100]
k = 10
print(minSum(arr, k))
using System;
using System.Collections.Generic;
public class GFG {
class MaxHeap {
List<int> heap = new List<int>();
public int Count
{
get { return heap.Count; }
}
public void Push(int val)
{
heap.Add(val);
int i = heap.Count - 1;
while (i > 0) {
int p = (i - 1) / 2;
if (heap[p] >= heap[i])
break;
int temp = heap[p];
heap[p] = heap[i];
heap[i] = temp;
i = p;
}
}
public int Pop()
{
int top = heap[0];
heap[0] = heap[heap.Count - 1];
heap.RemoveAt(heap.Count - 1);
int i = 0;
while (true) {
int largest = i;
int left = 2 * i + 1;
int right = 2 * i + 2;
if (left < heap.Count
&& heap[left] > heap[largest])
largest = left;
if (right < heap.Count
&& heap[right] > heap[largest])
largest = right;
if (largest == i)
break;
int temp = heap[i];
heap[i] = heap[largest];
heap[largest] = temp;
i = largest;
}
return top;
}
}
public int minSum(int[] arr, int k)
{
MaxHeap pq = new MaxHeap();
foreach(int x in arr) pq.Push(x);
int n = arr.Length;
// Perform n operations, each time reducing the
// largest element.
for (int i = 0; i < n && pq.Count > 0; i++) {
int cur = pq.Pop();
if (cur > k) {
if (cur < 10 * k)
cur -= k;
else
cur = (9 * cur) / 10;
pq.Push(cur);
}
// If cur <= k, it becomes 0 and is discarded.
}
int res = 0;
while (pq.Count > 0)
res += pq.Pop();
return res;
}
public static void Main()
{
int[] arr = { 90, 100 };
int k = 10;
GFG obj = new GFG();
Console.WriteLine(obj.minSum(arr, k));
}
}
function minSum(arr, k)
{
let pq = [];
for (let x of arr) {
pq.push(x);
}
pq.sort((a, b) => b - a);
let n = arr.length;
// perform n operations, each time reducing the largest
// element
for (let i = 0; i < n && pq.length > 0; i++) {
let cur = pq.shift();
if (cur > k) {
if (cur < 10 * k)
cur -= k;
else
cur = Math.floor((9 * cur) / 10);
pq.push(cur);
pq.sort((a, b) => b - a);
}
// if cur <= k, it becomes 0 and is discarded (not
// pushed back)
}
let res = 0;
while (pq.length > 0) {
res += pq.shift();
}
return res;
}
// Driver Code
let arr = [ 90, 100 ];
let k = 10;
console.log(minSum(arr, k));
Output
170