algorithmmathgreedy

Strange Bank(Atcoder Beginner contest 099)


To make it difficult to withdraw money, a certain bank allows its customers to withdraw only one of the following amounts in one operation:

1 yen (the currency of Japan)

6 yen, 6^2(=36) yen, 6^3(=216) yen, ...

9 yen, 9^2(=81) yen, 9^3(=729) yen, ...

At least how many operations are required to withdraw exactly N yen in total?

It is not allowed to re-deposit the money you withdrew.

Constraints 1≤N≤100000 N is an integer.

Input is given from Standard Input in the following format:

N Output If at least x operations are required to withdraw exactly N yen in total, print x.

Sample Input 1 127 Sample Output 1 4 By withdrawing 1 yen, 9 yen, 36(=6^2) yen and 81(=9^2) yen, we can withdraw 127 yen in four operations.

It seemed as a simple greedy problem to me ,So that was the approach I used, but I saw I got a different result for one of the samples and figured out, It will not always be greedy.

#include <iostream>
#include <queue>
#include <stack>
#include <algorithm>
#include <functional>
#include <cmath>

using namespace std;


int intlog(int base, long int x) {
return (int)(log(x) / log(base));
}
int main()
{
ios_base::sync_with_stdio(false);
cin.tie(NULL);

long int n;cin>>n;

int result=0;
while(n>0)
{
int base_9=intlog(9,n);int base_6=intlog(6,n);
int val;
val=max(pow(9,base_9),pow(6,base_6));
//cout<<pow(9,base_9)<<" "<<pow(6,base_6)<<"\n";
val=max(val,1);
if(n<=14 && n>=12)
    val=6;
n-=val;
//cout<<n<<"\n";
result++;
}
cout<<result;
return 0;
}

At n 14 and above 12 , we have to pick 6 rather than 9, because To reach zero it will take less steps.

It got AC only for 18/22 TCs Please help me understand my mistake.


Solution

  • Greedy will not work here as the choosing the answer greedily i.e. the optimal result at every step will not guarantee the best end result (you can see that in your example). So instead you should traverse through every possible scenarios at each step to figure out the overall optimal result.

    Now lets see how can we do that. As you can see that here the maximum input could be 10^5. And we can withdraw any one of the only following 12 values in one operation -

    [1, 6, 9, 36(=6^2), 81(=9^2), 216(=6^3), 729(=9^3), 1296(=6^4), 6561(=9^4), 7776(=6^5), 46656(=6^6), 59049(=9^5)]

    Because 6^7 and 9^6 will be more than 100000.

    So at each step with value n we will try to take each possible (i.e less than or equals to n) element arr[i] from the above array and then recursively solve the subproblem for n-arr[i] until we reach at zero.

    solve(n)
     if n==0 
      return 1;
     ans = n;
     for(int i=0;i<arr.length;i++)
       if (n>=arr[i])
         ans = min(ans, 1+solve(n-arr[i]);
     return ans;
    

    Now this is very time extensive recursive solution(O(n*2^12)). We will try to optimize it. As you will try with some sample cases you will come to know that the subproblems are overlapping that means there could be duplicate subproblems. Here comes Dynamic Programming into the picture. You can store every subproblem's solution so that we can re-use them in future. So we can modify our solution as following

    solve(n)
     if n==0 
      return 1; 
     ans = n;
     if(dp[n] is seen)
       return dp[n];
     for(int i=0;i<arr.length;i++)
       if (n>=arr[i])
         ans = min(ans, 1+solve(n-arr[i]);
     return dp[n] = ans;
    

    The time complexity for DP solution is O(n*12);