3

Given a coin n(<=10^9), I can exchange it for 3 coins:n/2,n/3 and n/4 (where / represents floor division). What is the greatest amount I can make? My code is:

#include <iostream>
using namespace std;
int a[10000000];
long int coin(long int n){
    if(n<10000000){
        return a[n];
    }
    else{
        return(max(n,coin(n/2)+coin(n/3)+coin(n/4)));
    }
}
int main()
{
    //cout << "Hello World!" << endl;
    long int n,ans;
    int i;
    a[0]=0;
    for(i=1;i<10000000;i++){
        a[i]=max(i,a[i/2]+a[i/3]+a[i/4]);
    }
    while(cin>>n){
        if(n<10000000){
            cout<<a[n]<<endl;
        }
        else{
            ans=coin(n);
            cout<<ans<<endl;
        }
    }
    return 0;
}

How can I improve its time and space complexity? Problem:https://www.hackerearth.com/problem/algorithm/bytelandian-gold-coins/description/

גלעד ברקן
  • 23,602
  • 3
  • 25
  • 61
Vivek Mangal
  • 532
  • 1
  • 8
  • 24
  • What exactly is your question? – CerebralFart Feb 07 '16 at 12:42
  • I want to improve it. I read somewhere that this can be done in O(log n) space complexity.So, What is the best way to solve this problem? – Vivek Mangal Feb 07 '16 at 12:43
  • ok updated my solution using maps for storing memoized values. now even space complexity is log(n) – phoenix Feb 07 '16 at 13:37
  • @Vivek: The basic idea to start from `a[0]` and then go up to `a[100..0]` is already correct and not that easy to improve. What you *could* do is to apply some kind of probabilistic analysis ... for example, don't store the numbers from `100..00 / 2 + 1` to `100..00`, but use a first division to get into the stored range. Basically, you could refrain from storing those numbers which are used once or only a few times ... next step would be to determine that numbers. – davidhigh Feb 07 '16 at 15:11
  • What is your goal? The linked task requires to value <=10 numbers in the range [0,10^9]. A [quick timing on coliru](http://coliru.stacked-crooked.com/a/0e2c69cccea470b5) requires roughly `0.2` seconds for a coin around 7*10^8 ... thus I guess you're already fast enough for the challenge. Is it just for curiosity? – davidhigh Feb 07 '16 at 15:59

1 Answers1

1

A few thoughts, no definite answer yet.

  • First, your approach is quite reasonable imo. You have numbers up to 10^9, which you cannot preprocess all. Instead, you take into account that the smaller numbers "somehow" are picked more often by the process, and so you memoize only up to a certain upper boundary, here 10^7.

  • An easy improvement in your basic algorithm is by realizing that you need to memoize only multiples of 2 or 3. All other inputs can easily be related to those numbers in the count function.

  • Another optimization could be to vary the upper bound 10^7 empirically. That is, choose some values between, say, 10^5 and 10^8 and then hand in the one with the minimum execution time.


Improving this basic approach is not trivial, but the way to improve it is by getting insight into the number selection procedure. Basically, one should memoize those numbers which are selected more often, and leave those numbers out which are picked only few times.

One could do a lot here, but usually the required results on which the memoization procedure is based have to be generated on-the-fly in the program which you hand in to the contest. I guess this makes it hard to come up with competitive solutions. I could imagine that simple rules of the form "memoize all below 10.000", "memoize multiples of 5 above 10.000", "memoize multiples of 7 above 10.000" and so on could be useful. Such rules can be easily encoded into the program without requiring too much memory. They could be found in advance by genetic algorithms, for example.


For an exact approach, one can assume a uniform distribution of the coin numbers in the problem. Then one can loop over all numbers i up to 10^9 and aquire how often each number k<i is chosen by the procedure. The result is an array count[i]. Next you pick a lower boundary L for count[i] and memoize all numbers i where count[i]>=L. However, as mentioned, this procedure is too costly as it has to be done in the run itself.

What you could do instead is to pick only, say, the N most-often picked numbers, and hard-code them in the code. The actual number N of included memoizaion numbers can be determined by the memory constraint in the task.

davidhigh
  • 14,652
  • 2
  • 44
  • 75