|
| 1 | +## Understanding The Problem Statement |
| 2 | + |
| 3 | +First we identify what the problem wants, we have n coin denomination or values A<sub>i</sub> where for my approach we take i as 0<=i<=n |
| 4 | +and n has the constraint 1<=n<=50, and each of the coin with value A is given a count or the number of these coins we have, which is C<sub>i</sub>. We are also provided |
| 5 | +K which is the value we wish to pay off using a valid coin combination. |
| 6 | + |
| 7 | +The question simply asks us to find the number of ways by which we can pay off the required value using our available coins. |
| 8 | + |
| 9 | +## Prerequisites |
| 10 | + |
| 11 | +- Basic Understanding of DP(Dynamic Programming) [DP-1 Jenny CS IT](https://youtu.be/lVR2u9lsxl8) |
| 12 | +- Preliminary Idea of Coin Change Problem [DP#2 Jenny CS IT](https://youtu.be/L27_JpN6Z1Q) |
| 13 | + |
| 14 | +## Solution Approach |
| 15 | + |
| 16 | +This problem is essentially a variation of the classic dynamic programming problem, coin change. Here we first recognize that we need to store the values for the coin denominations |
| 17 | +and the number of each of these coin types in two seperate arrays. Then we define the following formal definition of a recursive function: |
| 18 | + |
| 19 | +coinchange(index,current_value): returns the number of ways by which we can pay off the target cost with the given coin denominations, and number of coins of each type |
| 20 | + |
| 21 | +coinchange(index,current_value): { |
| 22 | + 1 if current_value=0 |
| 23 | + 0 if i=n(since we are using 0-based indexing) |
| 24 | + coinchange<sub>ix</sub>(i+1,current_value-ix\*A<sub>i</sub>) if 0<=ix<=C[i] and 0<=i<n |
| 25 | + } |
| 26 | +To explain this, we have two base cases if the current value becomes 0 then we return 1 as it represents 1 valid combination |
| 27 | +and if the index approaches becomes n we return 0 as we went out of the bounds of the array we initialized or out of bounds of the n we were given but still could not |
| 28 | +find a soltuion. The last statement involves a for loop from ix=0 upto ix<=C[i] being the number of coins of type A[i] we have and upon selecting each coin we make |
| 29 | +a recursive call to the next coin. A sample recursion tree is given below |
| 30 | + |
| 31 | +The test case for the given diagram is A={1,2,5} and C={3,2,2}. |
| 32 | +In the tree above we can see upon each call the child branches or the recursive calls direct to all possible combinations, starting from one coin if ix=0 we don't select |
| 33 | +that coin and move forward to the next coin, if ix=1 we select one of that specific coin and then move forward by substracting the total of the selected coins |
| 34 | +from the current value upon moving forward by making another recursive call. In this manner if ix=a we select a coins and then move to the next possible selection. In this |
| 35 | +manner we can check all possible combinations. But it is not necessary to brute force through all possible combinations. We can eliminate some of the redundant or |
| 36 | +repetitive calls by the use of [memoization](https://en.wikipedia.org/wiki/Memoization) which is essentially the process of storing computational results to prevent |
| 37 | +recalculating them which might cause unnecessary reptitive computation as mentioned earlier. This can be done using a 2-state dp, the states tend to be reflected by |
| 38 | +the number of arguments passed to the defined function which in this case is 2. So here we would take a 2D array with the size of rows as the maximum possible value for n +1 and |
| 39 | +columns as the maximum possible value for K +1. In each computation, we store the value result in the dp array as per **dp\[index]\[current_value]**. In future computations, |
| 40 | +if we find that the dp array contains our desired value we return it. Initially we set all the values to -1 in the dp array to easily identify if there is a result stored |
| 41 | +for that specific combination of index and current_value. A sample implementation is given below: |
| 42 | + |
| 43 | +## Solution |
| 44 | +```cpp |
| 45 | +#include<bits/stdc++.h> |
| 46 | +using namespace std; |
| 47 | +#define ll long long |
| 48 | +#define INF 100000009 |
| 49 | +#define modulo 100000007 |
| 50 | +#define Max_n 55 |
| 51 | +#define Max_K 1005 |
| 52 | +#define Max_C 25 |
| 53 | +int n; |
| 54 | +ll K; |
| 55 | +vector<ll>A,C; |
| 56 | + |
| 57 | +int dp[Max_n][Max_K]; |
| 58 | + |
| 59 | +ll coinchange(int i,int current_value){ |
| 60 | + if(current_value==0){ |
| 61 | + return 1; |
| 62 | + } |
| 63 | + if(i==n){ |
| 64 | + return 0; |
| 65 | + } |
| 66 | + if(dp[i][current_value]!=-1){ |
| 67 | + return dp[i][current_value]; |
| 68 | + } |
| 69 | + dp[i][current_value]=0; |
| 70 | + for(int ix=0;ix<=C[i] && current_value-ix*A[i]>=0;ix++){ |
| 71 | + dp[i][current_value]+=(coinchange(i+1,current_value-ix*A[i])%modulo); |
| 72 | + } |
| 73 | + return dp[i][current_value]%modulo; |
| 74 | + |
| 75 | +} |
| 76 | + |
| 77 | + |
| 78 | +void run_test_case(){ |
| 79 | + memset(dp,-1,sizeof(dp)); |
| 80 | + cin>>n>>K; |
| 81 | + A.resize(n); |
| 82 | + C.resize(n); |
| 83 | + for(ll &i:A){ |
| 84 | + cin>>i; |
| 85 | + } |
| 86 | + for(ll &i:C){ |
| 87 | + cin>>i; |
| 88 | + } |
| 89 | + cout<<coinchange(0,K)<<endl;//we pass our desired value that being K to the function as the current_value, this value is changed in every recursive call |
| 90 | +} |
| 91 | + |
| 92 | +int main(void){ |
| 93 | + int number_of_test_cases; |
| 94 | + cin>>number_of_test_cases; |
| 95 | + for(int current_case=1;current_case<=number_of_test_cases;current_case++){ |
| 96 | + cout<<"Case "<<current_case<<": "; |
| 97 | + run_test_case(); |
| 98 | + } |
| 99 | +} |
| 100 | +``` |
| 101 | +
|
0 commit comments