Open In App

Minimize Coin Removal for Bounded Pile Differences

Last Updated : 17 Jun, 2025
Comments
Improve
Suggest changes
Like Article
Like
Report

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:
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:
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. 

[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 <iostream>
#include <vector>
#include <algorithm>
#include <climits> 
using namespace std;

int minimumCoins(vector<int>& 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));

Output
2

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 <iostream>
#include <vector>
#include <algorithm>
using namespace std;


int minimumCoins(vector<int>& 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<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) {
        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));

Output
2



SDE Sheet - Coin Piles
Article Tags :
Practice Tags :

Similar Reads