Minimize Coin Removal for Bounded Pile Differences (original) (raw)

Last Updated : 17 Jun, 2025

Given an array **arr[] of integers, where each element represents the number of coins in a pile. You are also given an integer **k.
Your task is to remove the minimum number of **coins such that the absolute difference between the number of coins in any two updated piles is at most **k.

**Note: You can also remove a pile by removing all the coins of that pile.

**Examples:

**Input: arr[] = [2, 2, 2, 2], k= 0
**Output: 0
**Explanation: For all piles the difference in the number of coins is = 0. So, no need to remove any coins.

**Input: arr[] = [1, 5, 1, 2, 5, 1], k = 3
**Output: 2
**Explanation: If we remove one coin each from both the piles containing 5 coins, then for any two piles the absolute difference in the number of coins is <= 3.

Try It Yourselfredirect icon

[Approach]: Binary Search and Prefix Sum

We first sort the array to handle piles in order, then for each pile, we assume it to be the smallest remaining pile and compute the cost to remove all smaller piles entirely and trim larger ones to ensure no pile exceeds arr[i] + k. For each index, we efficiently calculate the coins to remove and select the minimum among them.

C++ ``

#include #include #include #include using namespace std;

int minimumCoins(vector& arr, int k) { // Sort the array to handle values in increasing order sort(arr.begin(), arr.end()); int n = arr.size();

// Prefix sum array to compute total coins quickly
vector<int> prefix(n, 0);
prefix[0] = arr[0];
for (int i = 1; i < n; i++)
    prefix[i] = prefix[i - 1] + arr[i];

int ans = INT_MAX, prev = 0;

for (int i = 0; i < n; i++) {
    // Update `prev` only when 
    // arr[i] is different from arr[i - 1] else continue
    
    // This represents total coins from piles smaller than arr[i]
    if (i > 0 && arr[i] == arr[i - 1])
        continue;
    
    if(i > 0){
        prev = prefix[i-1];
    }
    // Find the first index where arr[pos] > arr[i] + k
    int pos = upper_bound(arr.begin() + i, arr.end(), arr[i] + k) - arr.begin();


    // Total coins to remove:
    // - `prev` for coins from smaller piles (before i)
    // - `(prefix[n - 1] - prefix[pos - 1])` is the total 
    // coins after pos index
    // - coins that is allowed to stay in the range : [arr[i], arr[i] + k]
    int totalToRemove = prev + prefix[n - 1] - prefix[pos - 1] - (n - pos) * (arr[i] + k);

    // Update answer with minimum coins removed
    ans = min(ans, totalToRemove);
}

return ans;

}

int main(){

vector<int> arr = { 1, 5, 1, 2, 5, 1 };
int k = 3;
cout << minimumCoins(arr, k);
return 0;

}

Java

import java.util.*;

class GfG {

public static int minimumCoins(int[] arr, int k) {
    // Sort the array to handle values in increasing order
    Arrays.sort(arr); 
    int n = arr.length;

    // Prefix sum array to compute total coins quickly
    int[] prefix = new int[n];
    prefix[0] = arr[0];
    for (int i = 1; i < n; i++)
        prefix[i] = prefix[i - 1] + arr[i];

    int ans = Integer.MAX_VALUE, prev = 0;

    for (int i = 0; i < n; i++) {
        // Update `prev` only when 
        // arr[i] is different from arr[i - 1] else continue
        // This represents total coins from piles smaller than arr[i]
        if (i > 0 && arr[i] == arr[i - 1])
            continue;

        if (i > 0) {
            prev = prefix[i - 1];
        }

        // Find the first index where arr[pos] > arr[i] + k
        int pos = upperBound(arr, arr[i] + k, i, n);

        // Total coins to remove:
        // - `prev` for coins from smaller piles (before i)
        // - `(prefix[n - 1] - prefix[pos - 1])` is the total 
        // coins after pos index
        // - coins that is allowed to stay in the range : [arr[i], arr[i] + k]
        int totalToRemove = prev;
        if (pos < n) {
            totalToRemove += prefix[n - 1] - prefix[pos - 1] - (n - pos) * (arr[i] + k);
        }

        // Update answer with minimum coins removed
        ans = Math.min(ans, totalToRemove);
    }

    return ans;
}

private static int upperBound(int[] arr, int key, int low, int high) {
    while (low < high) {
        int mid = (low + high) / 2;
        if (arr[mid] <= key) {
            low = mid + 1;
        } else {
            high = mid;
        }
    }
    return low;
}

public static void main(String[] args) {
    int[] arr = {1, 5, 1, 2, 5, 1};
    int k = 3;
    System.out.println(minimumCoins(arr, k));
}

}

Python

import bisect

def minimumCoins(arr, k): # Sort the array to handle values in increasing order arr.sort() n = len(arr)

# Prefix sum array to compute total coins quickly
prefix = [0] * n
prefix[0] = arr[0]
for i in range(1, n):
    prefix[i] = prefix[i - 1] + arr[i]

ans = float('inf')
prev = 0

for i in range(n):
    # Update `prev` only when 
    # arr[i] is different from arr[i - 1] else continue
    # This represents total coins from piles smaller than arr[i]
    if i > 0 and arr[i] == arr[i - 1]:
        continue

    if i > 0:
        prev = prefix[i - 1]

    # Find the first index where arr[pos] > arr[i] + k
    pos = bisect.bisect_right(arr, arr[i] + k, i, n)

    # Total coins to remove:
    # - `prev` for coins from smaller piles (before i)
    # - `(prefix[n - 1] - prefix[pos - 1])` is the total 
    #  coins after pos index
    # - coins that is allowed to stay in the range : [arr[i], arr[i] + k]
    totalToRemove = prev
    if pos < n:
        totalToRemove += prefix[n - 1] - prefix[pos - 1] - (n - pos) * (arr[i] + k)

    # Update answer with minimum coins removed
    ans = min(ans, totalToRemove)

return ans

arr = [1, 5, 1, 2, 5, 1] k = 3 print(minimumCoins(arr, k))

C#

using System;

class GfG { public static int MinimumCoins(int[] arr, int k) { // Sort the array to handle values in increasing order Array.Sort(arr); int n = arr.Length;

    // Prefix sum array to compute total coins quickly
    int[] prefix = new int[n];
    prefix[0] = arr[0];
    for (int i = 1; i < n; i++)
        prefix[i] = prefix[i - 1] + arr[i];

    int ans = int.MaxValue, prev = 0;

    for (int i = 0; i < n; i++)
    {
        // Update `prev` only when 
        // arr[i] is different from arr[i - 1] else continue
        // This represents total coins from piles smaller than arr[i]
        if (i > 0 && arr[i] == arr[i - 1])
            continue;

        if (i > 0)
        {
            prev = prefix[i - 1];
        }

        // Find the first index where arr[pos] > arr[i] + k
        int pos = UpperBound(arr, arr[i] + k, i, n);

        // Total coins to remove:
        // - `prev` for coins from smaller piles (before i)
        // - `(prefix[n - 1] - prefix[pos - 1])` is the total 
        // coins after pos index
        // - coins that is allowed to stay in the range : [arr[i], arr[i] + k]
        int totalToRemove = prev;
        if (pos < n)
        {
            totalToRemove += prefix[n - 1] - prefix[pos - 1] - (n - pos) * (arr[i] + k);
        }

        // Update answer with minimum coins removed
        ans = Math.Min(ans, totalToRemove);
    }

    return ans;
}

public static int UpperBound(int[] arr, int key, int low, int high)
{
    while (low < high)
    {
        int mid = (low + high) / 2;
        if (arr[mid] <= key)
            low = mid + 1;
        else
            high = mid;
    }
    return low;
}

static void Main()
{
    int[] arr = { 1, 5, 1, 2, 5, 1 };
    int k = 3;
    Console.WriteLine(MinimumCoins(arr, k));
}

}

JavaScript

function minimumCoins(arr, k) { // Sort the array to handle values in increasing order arr.sort((a, b) => a - b); const n = arr.length;

// Prefix sum array to compute total coins quickly
const prefix = new Array(n).fill(0);
prefix[0] = arr[0];
for (let i = 1; i < n; i++)
    prefix[i] = prefix[i - 1] + arr[i];

let ans = Number.MAX_SAFE_INTEGER;
let prev = 0;

for (let i = 0; i < n; i++) {
    // Update `prev` only when
    // arr[i] is different from arr[i - 1] else continue
    // This represents total coins from piles smaller than arr[i]
    if (i > 0 && arr[i] === arr[i - 1])
        continue;

    if (i > 0) {
        prev = prefix[i - 1];
    }

    // Find the first index where arr[pos] > arr[i] + k
    const pos = upperBound(arr, arr[i] + k, i, n);

    // Total coins to remove:
    // - `prev` for coins from smaller piles (before i)
    // - `(prefix[n - 1] - prefix[pos - 1])` is the total 
    //  coins after pos index
    // - coins that is allowed to stay in the range : [arr[i], arr[i] + k]
    let totalToRemove = prev;
    if (pos < n) {
        totalToRemove += prefix[n - 1] - prefix[pos - 1] - (n - pos) * (arr[i] + k);
    }

    // Update answer with minimum coins removed
    ans = Math.min(ans, totalToRemove);
}

return ans;

}

function upperBound(arr, key, low, high) { while (low < high) { const mid = Math.floor((low + high) / 2); if (arr[mid] <= key) { low = mid + 1; } else { high = mid; } } return low; }

const arr = [1, 5, 1, 2, 5, 1]; const k = 3; console.log(minimumCoins(arr, k));

``

**Time Complexity: **O(n*log(n)) due to sorting and performing binary search for each of the n elements.
**Space complexity: **O(n) due to the prefix sum array used to store cumulative sums of the sorted array.

[Expected Approach] Two Pointer -O(n*log(n)) Time and O(1) Space

After sorting the array, we use a sliding window to try to keep the largest group of consecutive piles where the difference between the maximum and minimum is at most k. For each such window, we calculate how many coins need to be removed: we remove all coins from the piles before the window entirely, and from the piles after the window, we remove only the excess above the allowed maximum. By sliding this window across the array and checking each case, we keep track of the minimum total number of coins removed. This way, we ensure that we find the optimal result.

C++ `

#include #include #include using namespace std;

int minimumCoins(vector& arr, int k) { int n = arr.size();

sort(arr.begin(), arr.end());

int total = 0;
for (int x : arr)
    total += x;

int minRemoved = total;
int windowSum = 0;
int prefix = 0;
int end = 0;

for (int start = 0; start < n; start++) {

    // Expand the window to include elements 
    // within k difference from arr[start]
    while (end < n && arr[end] - arr[start] <= k) {
        windowSum += arr[end];
        end++;
    }

    // Calculate the upper limit allowed 
    // for pile values in the window
    int upper = arr[start] + k;

    int rightCount = n - end;

    // Calculate how many coins need 
    // to be removed from right-side piles
    int removeRight = (total - prefix - windowSum) - rightCount * upper;
    int removed = prefix + removeRight;
    minRemoved = min(minRemoved, removed);

    if (end == start) {
        end++;
    } else {
        windowSum -= arr[start];
    }

    prefix += arr[start];
}

return minRemoved;

}

int main() { vector arr = { 1, 5, 1, 2, 5, 1 }; int k = 3; cout << minimumCoins(arr, k);

return 0;

}

Java

import java.util.*;

class GfG {

public static int minimumCoins(int[] arr, int k) {
    int n = arr.length;

    Arrays.sort(arr);

    int total = 0;
    for (int x : arr)
        total += x;
        
    int minRemoved = total;
    int windowSum = 0;
    int prefix = 0;
    int end = 0;

    for (int start = 0; start < n; start++) {

        // Expand the window to include elements 
        // within k difference from arr[start]
        while (end < n && arr[end] - arr[start] <= k) {
            windowSum += arr[end];
            end++;
        }
        // Calculate the upper limit allowed 
        // for pile values in the window
        int upper = arr[start] + k;

        int rightCount = n - end;

        // Calculate how many coins need 
        // to be removed from right-side piles
        int removeRight = (total - prefix - windowSum) - rightCount * upper;
        int removed = prefix + removeRight;
        minRemoved = Math.min(minRemoved, removed);

        if (end == start) {
            end++;
        } else {
            windowSum -= arr[start];
        }

        prefix += arr[start];
    }

    return minRemoved;
}

public static void main(String[] args) {
    int[] arr = { 1, 5, 1, 2, 5, 1 };
    int k = 3;

    System.out.println(minimumCoins(arr, k));
}

}

Python

def minimumCoins(arr, k): n = len(arr)

arr.sort()

total = sum(arr)

min_removed = total
window_sum = 0
prefix = 0
end = 0

for start in range(n):

    # Expand the window to include elements 
    # within k difference from arr[start]
    while end < n and arr[end] - arr[start] <= k:
        window_sum += arr[end]
        end += 1

    # Calculate the upper limit allowed 
    # for pile values in the window
    upper = arr[start] + k
    right_count = n - end
    # Calculate how many coins need 
    # to be removed from right-side piles
    remove_right = (total - prefix - window_sum) - right_count * upper

    removed = prefix + remove_right

    min_removed = min(min_removed, removed)

    if end == start:
        end += 1
    else:
        window_sum -= arr[start]

    prefix += arr[start]

return min_removed

if name == "main": arr = [1, 5, 1, 2, 5, 1] k = 3

print(minimumCoins(arr, k))

C#

using System; using System.Linq;

class GfG {

public static int MinimumCoins(int[] arr, int k)
{
    int n = arr.Length;

    Array.Sort(arr);

    int total = arr.Sum();
    
    int minRemoved = total;
    int windowSum = 0;
    int prefix = 0;
    int end = 0;

    for (int start = 0; start < n; start++)
    {
        // Expand the window to include elements 
        // within k difference from arr[start]
        while (end < n && arr[end] - arr[start] <= k)
        {
            windowSum += arr[end];
            end++;
        }

        // Calculate the upper limit allowed 
        // for pile values in the window
        int upper = arr[start] + k;

        int rightCount = n - end;
        // Calculate how many coins need 
        // to be removed from right-side piles
        int removeRight = (total - prefix - windowSum) - rightCount * upper;
        int removed = prefix + removeRight;

        minRemoved = Math.Min(minRemoved, removed);

        if (end == start)
        {
            end++;
        }
        else
        {
            windowSum -= arr[start];
        }
        prefix += arr[start];
    }

    return minRemoved;
}

static void Main()
{
    int[] arr = { 1, 5, 1, 2, 5, 1 };
    int k = 3;

    Console.WriteLine(MinimumCoins(arr, k));
}

}

JavaScript

function minimumCoins(arr, k) { const n = arr.length; arr.sort((a, b) => a - b);

let total = arr.reduce((sum, val) => sum + val, 0);
let minRemoved = total;

let windowSum = 0;
let prefix = 0;
let end = 0;

for (let start = 0; start < n; start++) {
    // Expand the window to include elements 
    // within k difference from arr[start]
    while (end < n && arr[end] - arr[start] <= k) {
        windowSum += arr[end];
        end++;
    }

    // Calculate the upper limit allowed 
    // for pile values in the window
    let upper = arr[start] + k;

    let rightCount = n - end;

    // Calculate how many coins need 
    // to be removed from right-side piles
    let removeRight = (total - prefix - windowSum) - rightCount * upper;
    let removed = prefix + removeRight;
    minRemoved = Math.min(minRemoved, removed);

    if (end === start) {
        end++;
    } else {
        windowSum -= arr[start];
    }

    prefix += arr[start];
}

return minRemoved;

}

const arr = [1, 5, 1, 2, 5, 1]; const k = 3;

console.log(minimumCoins(arr, k));

`