Ritika has a coin worth n dollars. she can exchange any coin worth n at the bank into three separate coins worth n/2, n/3, and n/4 (each value rounded down, i.e., integer division). She keeps exchanging coins wherever it results in profit.
Find the maximum total amount of money Ritika can end up with.
Examples:
Input: n = 5
Output: 5
Explanation: 5 => 5 / 2 + 5 / 3 + 5 / 4 = 2 + 1 + 1 = 4 (< 5), so exchanging gives a loss.
Hence, the maximum amount she can have is 5 itself (no exchange).
Input: n = 12
Output: 13
Explanation: 12 => 12 / 2 + 12 / 3 + 12 / 4 = 6 + 4 + 3 = 13 (> 12), so she exchanges.
None of 6, 4, or 3 can be exchanged further for profit.
Hence, the maximum amount is 13.
Table of Content
[Naive Approach] Try Every Exchange Recursively - O(3^k) Time and O(k) Space
The idea is to recursively compute the maximum money obtained by either keeping the current coin or exchanging it into n/2, n/3, and n/4. Since the same coin values are solved repeatedly, this approach performs many duplicate computations.
#include <algorithm>
#include <iostream>
using namespace std;
int maxExchangeMoney(int n)
{
// Base case
if (n == 0)
return 0;
// Compute money obtained after exchange
int exchanged = maxExchangeMoney(n / 2) + maxExchangeMoney(n / 3) + maxExchangeMoney(n / 4);
// Return the better option
return max(n, exchanged);
}
int main()
{
int n = 12;
cout << maxExchangeMoney(n);
return 0;
}
class GFG {
public int maxExchangeMoney(int n)
{
// Base case
if (n == 0)
return 0;
// Compute money obtained after exchange
int exchanged = maxExchangeMoney(n / 2)
+ maxExchangeMoney(n / 3)
+ maxExchangeMoney(n / 4);
// Return the better option
return Math.max(n, exchanged);
}
public static void main(String[] args)
{
int n = 12;
GFG obj = new GFG();
System.out.println(obj.maxExchangeMoney(n));
}
}
def maxExchangeMoney(n):
# Base case
if n == 0:
return 0
# Compute money obtained after exchange
exchanged = maxExchangeMoney(
n // 2) + maxExchangeMoney(n // 3) + maxExchangeMoney(n // 4)
# Return the better option
return max(n, exchanged)
if __name__ == '__main__':
n = 12
print(maxExchangeMoney(n))
using System;
class GFG {
public int maxExchangeMoney(int n)
{
// Base case
if (n == 0)
return 0;
// Compute money obtained after exchange
int exchanged = maxExchangeMoney(n / 2)
+ maxExchangeMoney(n / 3)
+ maxExchangeMoney(n / 4);
// Return the better option
return Math.Max(n, exchanged);
}
static void Main()
{
int n = 12;
GFG obj = new GFG();
Console.WriteLine(obj.maxExchangeMoney(n));
}
}
function maxExchangeMoney(n)
{
// Base case
if (n === 0)
return 0;
// Compute money obtained after exchange
let exchanged = maxExchangeMoney(Math.floor(n / 2))
+ maxExchangeMoney(Math.floor(n / 3))
+ maxExchangeMoney(Math.floor(n / 4));
// Return the better option
return Math.max(n, exchanged);
}
// Driver Code
let n = 12;
console.log(maxExchangeMoney(n));
Output
13
[Better Approach] Dynamic Programming (Memoization using Array) - O(n) Time and O(n) Space
The idea is to store the maximum money for every coin value in a DP array. Before solving a coin, check whether its answer is already computed. This avoids repeated recursive calculations.
Working of Approach:
- Create a DP array dp of size n + 1 and initialize all values to -1 to indicate that no state has been computed yet.
- Recursively compute the maximum money for a coin of value n. Before solving a state, check whether its value is already stored in dp.
- If the value is not computed, recursively find the maximum money obtainable from n/2, n/3, and n/4, and add their results.
- Compare the exchanged value with the original coin value and store the larger value in dp[n].
- Return the stored result. Since each coin value is computed only once, repeated recursive calculations are avoided.
#include <algorithm>
#include <iostream>
#include <vector>
using namespace std;
vector<int> dp;
// Function to return maximum exchange money
int findMaxExc(int n)
{
// Base case
if (n == 0)
return 0;
// Return already computed answer
if (dp[n] != -1)
return dp[n];
// Compute money obtained after exchange
int exchanged = findMaxExc(n / 2) + findMaxExc(n / 3) + findMaxExc(n / 4);
// Store and return the best answer
return dp[n] = max(n, exchanged);
}
int maxExchangeMoney(int n)
{
// Initialize DP array
dp.assign(n + 1, -1);
return findMaxExc(n);
}
int main()
{
int n = 12;
cout << maxExchangeMoney(n);
return 0;
}
import java.util.Arrays;
class GFG {
// DP array to store previously computed results
int[] dp;
// Function to return maximum exchange money
int findMaxExc(int n)
{
// Base case
if (n == 0)
return 0;
// Return already computed answer
if (dp[n] != -1)
return dp[n];
// Compute money obtained after exchange
int exchanged = findMaxExc(n / 2) + findMaxExc(n / 3) + findMaxExc(n / 4);
// Store and return the best answer
return dp[n] = Math.max(n, exchanged);
}
// Function to return maximum exchange money
public int maxExchangeMoney(int n)
{
// Initialize DP array
dp = new int[n + 1];
Arrays.fill(dp, -1);
return findMaxExc(n);
}
public static void main(String[] args)
{
int n = 12;
GFG obj = new GFG();
System.out.println(obj.maxExchangeMoney(n));
}
}
dp = [-1]
# Function to return maximum exchange money
def findMaxExc(n):
# Base case
if n == 0:
return 0
# Return already computed answer
if dp[n] != -1:
return dp[n]
# Compute money obtained after exchange
exchanged = findMaxExc(n // 2) + findMaxExc(n // 3) + findMaxExc(n // 4)
# Store and return the best answer
dp[n] = max(n, exchanged)
return dp[n]
def maxExchangeMoney(n):
# Initialize DP array
global dp
dp = [-1] * (n + 1)
return findMaxExc(n)
if __name__ == '__main__':
n = 12
print(maxExchangeMoney(n))
using System;
class GFG {
// DP array to store previously computed results
int[] dp;
// Function to return maximum exchange money
int findMaxExc(int n)
{
// Base case
if (n == 0)
return 0;
// Return already computed answer
if (dp[n] != -1)
return dp[n];
// Compute money obtained after exchange
int exchanged = findMaxExc(n / 2) + findMaxExc(n / 3) + findMaxExc(n / 4);
// Store and return the best answer
return dp[n] = Math.Max(n, exchanged);
}
// Function to return maximum exchange money
public int maxExchangeMoney(int n)
{
// Initialize DP array
dp = new int[n + 1];
Array.Fill(dp, -1);
return findMaxExc(n);
}
static void Main()
{
int n = 12;
GFG obj = new GFG();
Console.WriteLine(obj.maxExchangeMoney(n));
}
}
let dp = [];
// Function to return maximum exchange money
function findMaxExc(n)
{
// Base case
if (n === 0) {
return 0;
}
// Return already computed answer
if (dp[n] !== -1) {
return dp[n];
}
// Compute money obtained after exchange
let exchanged = findMaxExc(Math.floor(n / 2)) + findMaxExc(Math.floor(n / 3)) + findMaxExc(Math.floor(n / 4));
// Store and return the best answer
return dp[n] = Math.max(n, exchanged);
}
function maxExchangeMoney(n)
{
// Initialize DP array
dp = new Array(n + 1).fill(-1);
return findMaxExc(n);
}
// Driver Code
let n = 12;
console.log(maxExchangeMoney(n));
Output
13
[Expected Approach] Dynamic Programming (Memoization using Hash Map) - O(S) Time and O(S) Space
The idea is to use an unordered_map to memoize only the coin values that are actually visited during recursion. This avoids duplicate computations while using much less memory than a DP array, making it suitable for large values of n.
Let us understand with an example:
Input: n = 12
- Start with n = 12. Since 12 ≥ 12, recursively compute maxExchangeMoney(6) + maxExchangeMoney(4) + maxExchangeMoney(3).
- The values 6, 4, and 3 are all smaller than 12, so they cannot be exchanged further and return 6, 4, and 3 respectively.
- The total money obtained after exchange is 6 + 4 + 3 = 13.
- Compare the exchanged value with the original coin value: max(12, 13) = 13.
- Store 13 in the hash map for coin 12 and return 13 as the final answer.
#include <algorithm>
#include <iostream>
#include <unordered_map>
using namespace std;
// map to store previously calculated
// results for optimization
unordered_map<int, int> mp;
// Function to calculate the maximum exchange money
int maxExchangeMoney(int n)
{
// if result for n is already computed,
// return it directly
if (mp.find(n) != mp.end())
return mp[n];
// base case: coins smaller than 12 cannot
// be exchanged for profit
if (n < 12)
return n;
// recursively compute exchange value by
// splitting into n/2, n/3, n/4
int change = maxExchangeMoney(n / 2) + maxExchangeMoney(n / 3) + maxExchangeMoney(n / 4);
// take the maximum of exchanged value and
// the original coin value
change = max(change, n);
// store result in map before returning
mp[n] = change;
return change;
}
int main()
{
int n = 12;
cout << maxExchangeMoney(n);
return 0;
}
import java.util.HashMap;
class GFG {
// Map to store previously computed results
HashMap<Integer, Integer> memo = new HashMap<>();
// Function to calculate the
// maximum exchange money
public int maxExchangeMoney(int n)
{
// Return the stored result if
// already computed
if (memo.containsKey(n))
return memo.get(n);
// Coins smaller than 12 cannot
// be exchanged for profit
if (n < 12)
return n;
// Compute the maximum value obtained after
// exchanging the coin
int exchange = maxExchangeMoney(n / 2)
+ maxExchangeMoney(n / 3)
+ maxExchangeMoney(n / 4);
// Choose the better option between exchanging and
// keeping the coin
int result = Math.max(n, exchange);
// Store the result for future use
memo.put(n, result);
return result;
}
public static void main(String[] args)
{
int n = 12;
GFG obj = new GFG();
System.out.println(obj.maxExchangeMoney(n));
}
}
from math import floor
# map to store previously calculated
# results for optimization
mp = {}
# Function to calculate the maximum
# exchange money
def maxExchangeMoney(n):
# if result for n is already computed,
# return it directly
if n in mp:
return mp[n]
# base case: coins smaller than 12 cannot
# be exchanged for profit
if n < 12:
return n
# recursively compute exchange value by
# splitting into n/2, n/3, n/4
change = maxExchangeMoney(
floor(n / 2)) + maxExchangeMoney(floor(n / 3)) + maxExchangeMoney(floor(n / 4))
# take the maximum of exchanged value
# and the original coin value
change = max(change, n)
# store result in map before returning
mp[n] = change
return change
if __name__ == '__main__':
n = 12
print(maxExchangeMoney(n))
using System;
using System.Collections.Generic;
class GFG {
// Map to store previously computed results
Dictionary<int, int> memo = new Dictionary<int, int>();
// Function to calculate the maximum
// exchange money
public int maxExchangeMoney(int n)
{
// Return the stored result if already computed
if (memo.ContainsKey(n))
return memo[n];
// Coins smaller than 12 cannot be
// exchanged for profit
if (n < 12)
return n;
// Compute the maximum value obtained after
// exchanging the coin
int exchange = maxExchangeMoney(n / 2)
+ maxExchangeMoney(n / 3)
+ maxExchangeMoney(n / 4);
// Choose the better option between exchanging and
// keeping the coin
int result = Math.Max(n, exchange);
// Store the result for future use
memo[n] = result;
return result;
}
static void Main()
{
int n = 12;
GFG obj = new GFG();
Console.WriteLine(obj.maxExchangeMoney(n));
}
}
// map to store previously calculated results for
// optimization
const mp = new Map();
// Function to calculate the
// maximum exchange money
function maxExchangeMoney(n)
{
// if result for n is already computed, return it
// directly
if (mp.has(n)) {
return mp.get(n);
}
// base case: coins smaller than 12
// cannot be exchanged for profit
if (n < 12) {
return n;
}
// recursively compute exchange value
// by splitting into n/2, n/3, n/4
let change = maxExchangeMoney(Math.floor(n / 2))
+ maxExchangeMoney(Math.floor(n / 3))
+ maxExchangeMoney(Math.floor(n / 4));
// take the maximum of exchanged value and the original
// coin value
change = Math.max(change, n);
// store result in map before returning
mp.set(n, change);
return change;
}
// Driver Code
const n = 12;
console.log(maxExchangeMoney(n));
Output
13