0

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]; 
}

1 Answers1

0

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.

Mohd
  • 5,523
  • 7
  • 19
  • 30
  • Thanks, the solution worked with 2D vector but with map stl it still gives TLE. Consider the 3 arguments (type int int string) then how does map, reserve would work? and I have a 1 more doubt how to estimate time complexity in case of memoization. For example - If we have 3 arguments then time complexity is O(arg1*arg2*arg3) ? – utkarsh bajpai Mar 15 '20 at 11:25
  • About time complexity, it depends on the state (just like looping through all states in bottom up approach) so yes if you have 3 arguments as the dp state then your time complexity will be `O(arg1 * arg2 * arg3)` – Mohd Mar 15 '20 at 11:34
  • And regarding your first question, lets say you have coins vector of size `10` and total amount `100` then your states will be `10*100` total so you will use `map.reserve(1000)` [This blog](https://codeforces.com/blog/entry/21853) and [This](https://codeforces.com/blog/entry/62393) explain everything you need to know about `unordered_map` – Mohd Mar 15 '20 at 11:40
  • Thanks for pointing out the resources. Can you please show the implementation of memoization using map.reserve method using the above code? Share the links of your implementation if possible. – utkarsh bajpai Mar 15 '20 at 12:26
  • It will be the same as your code but when you initialize the map use `unordered_map lookup; lookup.reserve(n*W);` – Mohd Mar 15 '20 at 21:50
  • The above modification still gives TLE. Check my submission for leetcode - https://leetcode.com/submissions/detail/312991669/ – utkarsh bajpai Mar 16 '20 at 12:15
  • The link is broken. Like I mentioned above, you should use array/vector for lookup unless in very rare cases where array can't be used. `map.reserve()` optimizes the look up time but it is still not worst case `O(1)` – Mohd Mar 16 '20 at 17:24
  • check out the new link and run the code in Leetcode - https://ide.geeksforgeeks.org/CuMMRDk8Ei. There are problems which have 3 arguments or string is there in the argument then 2D array concept won't work. – utkarsh bajpai Mar 17 '20 at 10:43
  • The code is fine, if it doesn't pass then the problem is designed so you use an array. If you have 3 or more arguments you could use 3d or Kd array as long as it fits in memory. If the problem requires you to use map then the time constraint will be increased. – Mohd Mar 17 '20 at 23:07