Search code examples
c++algorithmdata-structuresdynamic-programmingcoin-change

Min Coin Change - Dynamic Programming


Question - You are given coins of different denominations and a total amount of money amount. Write a function to compute the fewest number of coins that you need to make up that amount.Infinite supply of coin is there.

My Approach - I have followed the top-down approach but with memoization using map stl, I am getting TLE. Please help in finding out the error and estimating the time complexity.

Here is my code -

// for example coins v = {1,2} and W = 2 then possible solutions are -
// (2) or (1, 1), therefore minimum number of coins required is 1.
// Below is the function calculating the minimum number of coins required for change

int minCoinChange(vector<int> &v, int start, int W, unordered_map<string, int> &lookup){

// v denoting the vector conataining coins with given denomination
// start denoting the start index i.e. 0(initially)
// W denoting the required change
// lookup is map stl for storing values.

// base cases 
    if(W<0){
        return INT_MAX;
    }
    if(W == 0){
        return 0;
    }
    if(start >= v.size()){
        return INT_MAX;
    }

// for memoization creating the "key"
    string key = to_string(start) + '|' + to_string(W);

// if the key is not found in map then go inside if 
    if(lookup.find(key) == lookup.end()){
        int excl = minCoinChange(v, start+1, W, lookup); // if element is excluded

        int incl = 1 + minCoinChange(v, start, W - v[start], lookup); if element is included 

        lookup[key] = min(incl, excl); // calculating minimum number of coins required and storing in map 
    }
// if key is already there then return value stored in map 
    return lookup[key]; 
}


Solution

  • First of all, unordered_map has a worst case O(n) look up time. There are ways to optimize unordered_map performance like reserving number of buckets with map.reserve(n) with n being the number of unique items you expect to have in the map.

    You can use map instead which has worst case O(log(n)) look up time, but since the time complexity of that algorithm is O(n * W) your n and W will be small enough to fit in an array and then you will have O(1) look up instead.

    Below is modification of your function to use array look up instead:

    int minCoinChange(vector<int> &v, int start, int W, vector<vector<int>> &lookup){
    
        // v denoting the vector conataining coins with given denomination
        // start denoting the start index i.e. 0(initially)
        // W denoting the required change
        // lookup is 2d vector for storing already computed values.
    
        // base cases 
        if (W<0) {
            return 1e9;
        }
        if (W == 0) {
            return 0;
        }
        if (start >= v.size()) {
            return 1e9;
        }
    
        // if this state wasn't computed before
        if (lookup[start][W] == -1) {
            int excl = minCoinChange(v, start+1, W, lookup); // if element is excluded
    
            int incl = 1 + minCoinChange(v, start, W - v[start], lookup); // if element is included 
    
            lookup[start][W] = min(incl, excl); // calculating minimum number of coins required and storing in map 
        }
        // return the saved value
        return lookup[start][W];
    }
    

    Note:

    For base cases, instead of using INT_MAX which will overflow if you add 1 to it, use something like 10^9.