Last Updated: 11 Dec, 2021

Minimum Cost to Buy Rice

Moderate
Asked in company
Adobe

Problem statement

It’s year-end and also the perfect time to have a Ninja gathering. You are planning to invite your Ninja friends for a delightful rice meal.

The town you live in has exactly ‘N’ shops selling rice. The shops are numbered from 1, 2, 3 … N, and the i’th shop sells rice bags that weigh exactly i kgs. Each shop has an unlimited supply of the bags that they sell so you don’t have to worry about that. You are also given an array ‘COST’ where COST[i] denotes the price at which the i’th shop sells one bag of rice. Note that if COST[i] is equal to -1, then it means that the i’th shop is closed and you cannot buy rice from that shop (The array ‘COST’ follows 0-based indexing).

You have to buy exactly ‘W’ kgs of rice, you are not willing to buy extra rice to avoid any wastage. Find the minimum cost to complete this task, if it is not possible to complete the task then print “-1”.

Example :
If ‘N’ = 5, ‘W‘ = 4 and “COST’’ = {100, 5, -1, 100, 100}.

Then, we can buy two bags of rice from the 2’nd shop. This shop sells rice in packing of 2kgs each and sells each bag for COST[1] = 5. Therefore we have to pay a total cost of 2 * 5 = 10 to complete this task.
Note that there are more ways to complete this task. For example, we can buy four bags of rice from the 1’st shop, this will have a cost of 4 * COST[0] = 400 which is much greater than the cost of 10 calculated earlier. Therefore we will print 10.
Input Format :
The first line contains a single integer ‘T’ denoting the number of test cases, then each test case follows:

The first line of each test case contains two integers ‘N’ and ‘W’, denoting the number of shops and the weight of rice respectively.

The next lines contain N integers ‘COST’, denoting the price charged by each shop for one bag of rice.
Output Format :
For each test case, print the minimum cost to complete the task.

Output for each test case will be printed on a separate line.
Note :
You are not required to print anything; it has already been taken care of. Just implement the function.
Constraints :
1 ≤ ‘T’ ≤ 10      
1 ≤ N ≤ 100
1 ≤ W ≤ 500
-1 ≤ COST[i] ≤ 10^5 

Time limit: 1 sec

Approaches

01 Approach

One will get a clear intuition that a simple greedy approach will not work. A naive recursive approach that can later be extended to implement a DP solution is discussed here.

 

Let (i, j) recursive call return the minimum cost to buy exactly ‘j’ kgs of rice only from the first ‘i’ shops, then to calculate (i, j): we can avoid buying anything from the i’th shop, in this case, we will make a transition from (i-1, j) -> (i, j). If in case we plan to buy rice bags from the i’th shop then there can be two conditions, the first one is the case where we are buying from the i’th shop for the first time, in this case, we will make a transition of (i-1, j-i) -> (i, j). Another case here can be, when we have already bought a bag from the i’th shop and plan to buy more bags from the i’th shop now, in this case, in this case, we will make a transition from (i, j-i) -> (i, j). Remember that the answer for (i, j) will be the minimum value amongst the three transitions discussed above.

 

Our final answer will be the minimum of all the recursive calls (i, W) for 1 ≤ i ≤ N.

 

The steps are as follows :

  1. Initialize a variable ‘Price’ to INT_MAX, this variable will store the final answer.
  2. Make initial recursive calls (i, W) for all 1 ≤ i ≤ N, update the value of ‘Price’ so that it stores the minimum value returned by these recursive calls.
  3. In the recursive function:
    • End the recursion using base condition when either ‘i’ or ‘j’ equals 0.
    • Initialize a variable ‘curAns’ to store the answer for the (i, j) recursive call.
    • Do the first transition from (i-1, j) to (i, j).
    • If the Cost[i-1] is not equal to -1, then do the other two transitions: (i-1, j-i) to (i, j) and (i, j-i) to (i, j).
    • Update ‘curAns’ to store the minimum value of all these three transitions, and finally return this value to end the current recursive call.
  4. Finally, check if ‘Price’ is still equal to INT_MAX, this means that it is not possible to buy the rice and we will return -1 in this case. Else return the value stored in ‘Price’.

02 Approach

One will get a clear intuition that a simple greedy approach will not work. This is a problem that can easily be solved using dynamic programming. Dynamic programming involves figuring out the states of the DP and building a transition logic to calculate the state. It is highly suggested to have a basic idea about dynamic programming before solving this problem.

 

Let DP[i][j] store the minimum cost to buy exactly ‘j’ kgs of rice only from the first ‘i’ shops. Suppose we have already calculated the answer for all the DP-matrix values till ‘i-1’. Now we have to move further and include the contribution of the i’th shop, then to calculate DP[i][j] for all ‘j’ values belonging to [0, W] we need to build some transition logic.

We have many choices, let's explore each one of them. We can avoid buying anything from the i’th shop, in this case, the value DP[i][j] will be equal to DP[i-1][j], this is because we will make a transition from (i-1, j) -> (i, j). If in case we plan to buy rice bags from the i’th shop then there can be two conditions, the first one is the case where we are buying from the i’th shop for the first time, in this case, DP[i][j] = DP[i-1][j-i] + COST[i], this is because we will make a transition from (i-1, j-i) -> (i, j). Another case here can be, when we have already bought a bag from the i’th shop and plan to buy more bags from the i’th shop now, in this case, DP[i][j] = DP[i][j-i] + COST[i], this is because we will make a transition from (i, j-i) -> (i, j). Remember that we will store the minimum of the above three transitions finally in DP[i][j].

We can finally return the minimum of DP[i][W] where 1 ≤ i ≤ N.

 

To implement the above logic recursively, initialize all the values in the DP matrix equal to a very large number, as we will be using the operation to find the minimum of the three transitions discussed above, a very large initial value will avoid the altering of the answer because of the initialized value, though we will initialize the first row (corresponding to i=0) with a value equal to 0, this will be used as a base condition to end the recursive calls. Now make recursive calls to find the answer when the total weight is equal to ‘W’, ie: make initial calls to calculate DP[i][W] for all 1 ≤ i ≤ N, and return the minimum of these recursive calls. Inside the recursive function, calculate the current state from the three transitions discussed above. Also, remember to memoize the solution to avoid recomputation.

 

The steps are as follows :

  1. Declare a ‘DP’ matrix of size (N + 1) * (W + 1). Initialize all the cells to INT_MAX, except the first column. Initialize all the cells in the first column (corresponding to a weight equal to 0) to value 0.
  2. Initialize a variable ‘Price’ to INT_MAX, this variable will store the final answer.
  3. Make initial recursive calls (i, W) for all 1 ≤ i ≤ N, update the value of ‘Price’ so that it stores the minimum value returned by these recursive calls.
  4. In the recursive function:
    • End the recursion using base condition when either ‘i’ or ‘j’ equals 0.
    • If the value of DP[i][j] is not equal to INT_MAX, then return the recursive call as we have already calculated the value for this and stored that in DP[i][j].
    • Initialize a variable ‘curAns’ to store the answer for the (i, j) recursive call.
    • Do the first transition from (i-1, j) to (i, j).
    • If the Cost[i-1] is not equal to -1, then do the other two transitions: (i-1, j-i) to (i, j) and (i, j-i) to (i, j).
    • Update ‘curAns’ to store the minimum value of all these three transitions, and finally, return this value to end the current recursive call. Also, store this value in DP[i][j] so that we don’t have to recalculate it in future.

Finally, check if ‘Price’ is still equal to INT_MAX, this means that it is not possible to buy the rice and we will return -1 in this case. Else return the value stored in ‘Price’.

03 Approach

One will get a clear intuition that a simple greedy approach will not work. This is a problem that can easily be solved using dynamic programming. Dynamic programming involves figuring out the states of the DP and building a transition logic to calculate the state. It is highly suggested to have a basic idea about dynamic programming before solving this problem.

 

Let DP[i][j] store the minimum cost to buy exactly ‘j’ kgs of rice only from the first ‘i’ shops. Suppose we have already calculated the answer for all the DP-matrix values till ‘i-1’. Now we have to move further and include the contribution of the i’th shop, then to calculate DP[i][j] for all ‘j’ values belonging to [0, W] we need to build some transition logic.

 

We have many choices, let's explore each one of them. We can avoid buying anything from the i’th shop, in this case, the value DP[i][j] will be equal to DP[i-1][j], this is because we will make a transition from (i-1, j) -> (i, j). If in case we plan to buy rice bags from the i’th shop then there can be two conditions, the first one is the case where we are buying from the i’th shop for the first time, in this case, DP[i][j] = DP[i-1][j-i] + COST[i], this is because we will make a transition from (i-1, j-i) -> (i, j). Another case here can be, when we have already bought a bag from the i’th shop and plan to buy more bags from the i’th shop now, in this case, DP[i][j] = DP[i][j-i] + COST[i], this is because we will make a transition from (i, j-i) -> (i, j). Remember that we will store the minimum of the above three transitions finally in DP[i][j].

We can finally return the minimum of DP[i][W] where 1 ≤ i ≤ N.

 

We can easily implement the above logic. Initialize all the values in the DP matrix equal to a very large number, as we will be using the operation to find the minimum of the three transitions discussed above, a very large initial value will avoid the altering of the answer because of the initialized value, though we will initialize the first row (corresponding to i=0) with a value equal to 0.

Now to calculate the cells of the DP matrix, run two ‘for’ loops, the first ‘for’ loop for the variable ‘i’ from 1 to N, and the second one for the variable ‘j’ from 0 to W. Finally, return the minimum of DP[i][W] where 1 ≤ i ≤ N.

 

Note that for the transition of the i’th state we are only using the i-1’th state, therefore we can reduce the space complexity by only storing the previous row (corresponding to the i-1’th row) to calculate the i’th row. This reduces the space needed from N*W to N as we no longer store all the values of the DP matrix. This is a standard method of reducing the space complexity of many questions that are solved using DP, though for the sake of simplicity we have not used this code in our editorial.

 

The steps are as follows :

  1. Declare a ‘DP’ matrix of size (N + 1) * (W + 1). Initalize all the cells to INT_MAX, except the first column. Initialize all the cells in the first column (corresponding to weight equal to 0) to value 0.
  2. Fill in the DP matrix, run an outer for loop for variable ‘i’ from 1 to N, and run an inner for loop for variable ‘j’ from 1 to W:
    • Consider the first transition from (i-1, j) to (i, j).
    • If the Cost[i-1] is not equal to -1, then consider the other two transitions: (i-1, j-i) to (i, j) and (i, j-i) to (i, j).
    • Update DP[i][j] to store the minimum value of all these three transitions.
  3. Initialize a variable ‘Price’ to INT_MAX, this variable will store the final answer.
  4. Run a for loop for variable ‘i’ from 1 to N, update the value of ‘Price’ to store the minimum value of DP[i][W].
  5. Finally, check if ‘Price’ is still equal to INT_MAX, this means that it was not possible to buy the rice and we will return -1 in this case. Else return the value stored in ‘Price’.