Closed. This question does not meet Stack Overflow guidelines. It is not currently accepting answers.
Questions asking for code must demonstrate a minimal understanding of the problem being solved. Include attempted solutions, why they didn't work, and the expected results. See also: Stack Overflow question checklist
Closed 9 years ago.
Improve this question
I am self-learning C++ from Sams Teach Yourself C++ In One Hour A Day and on page 150 the author discusses recursive functions using the Fibonacci Series.
He uses the following code:
#include <iostream>
using namespace std;
int GetFibNumber(int FibIndex)
{
if(FibIndex < 2 )
return FibIndex;
else
return GetFibNumber(FibIndex - 1) + GetFibNumber(FibIndex - 2);
}
int main()
{
cout << " Enter 0 based index of desired Fibonacci Number: ";
int Index = 0;
cin >> Index;
cout << " Fibonacci number is: " << GetFibNumber(Index) << endl;
return 0;
}
What is the difference between having
return GetFibNumber(FibIndex - 1) + GetFibNumber(FibIndex - 2);
and
return FibIndex - 1 + FibIndex - 2;
Why do you have to call the function inside itself?
Thank you in advance!
You ask: "Why do you have to call the function inside itself?" Well, strictly, you don't.
The Fibonacci sequence is the sequence of numbers defined by this mathematical recursion:
a0 = 0
a1 = 1
an = an-1 + an-2
The original function does compute this sequence, albeit inefficiently. If Index is 0 it returns 0; if it is 1, it returns 1. Otherwise it returns GetFibNumber(Index - 1) + GetFibNumber(Index - 2), which is precisely what the mathematical definition is. For each element of the sequence, you must add the two previous terms in the sequence.
Your code just returns Index - 1 + Index - 2, which will give a different numerical sequence. Compare:
Fibonacci: 0, 1, 1, 2, 3, 5, 8, 13, 21, 36...
Yours: 0, 1, 1, 3, 5, 7, 9, 11, 13, 17...
But that aside, you do not strictly need a recursive function to compute this mathematical recursion. All you need is a simple for loop:
int GetFibNumber(int FibIndex)
{
if(FibIndex < 2 )
return FibIndex;
int a_n_2 = 0, a_n_1 = 1, a_n;
for (i = 2; i < FibIndex; i++)
{
a_n = a_n_1 + a_n_2;
a_n_2 = a_n_1;
a_n_1 = a_n;
}
return a_n;
}
This approach will be much faster, also.
The code-recursive technique is mathematically correct. It is, however, slower, because it doesn't reuse any computations. It computes an-1 by reworking the recursion all the way back to a1. It then computes an-2, without reusing any of the work that generated an-1. And if you consider this lack-of-reuse happens at every step of the recursion, you'll see that the running time grows exponentially for the recursive function. It grows linearly for the for loop, though.
Advanced topic: There is a way to make the recursive version run faster, and that can be important to know if you run into a programming problem that is most readily defined recursively. Once you're much more comfortable with C++, look up memoization. A memoized recursive Fibonacci gives linear worst-case run-time, and constant run time for repeated lookups (assuming the memo lookup is itself O(1)).
Using the version that does not use recursion is not correct. It will only compute correctly first few Fiboonacci numbers. Try to compute first 10 Fibonacci numbers using the two versions and you will see yourself the two versions compute two different sequences.
The function GetFibNumber calculates the Nth number in the Fibonacci series. If you just take a look the explanation on http://en.wikipedia.org/wiki/Fibonacci_number it is calculated by adding the Nth-1 and Nth-2 numbers in the Fibinacci series. And this is exactly what the function does. You provide the function with an index in the Fibonacci series that you want to calculate (lets say 6; this should have 8 as result).
To calculate the 6th element in the Fibonacci series you need to add the 5th and 4th elements together. So you first need to calculate those. This is where recursion steps in. You can let the function call itself; but instead of calling it again with the value 6 as parameter you now use 5 and 4. This will again lead to the same problem (you need to calc 5th element by adding elements 4 and 3), etc. etc.
With the recursive function you can simply re-use the code to perform the same calculation over and over again until you reach a certain point where you have an answer for the calculation (in this case if N = 1 or N = 0; these cases will result in 1).
I would suggest, since you are still learning, to program this both recursively (like the author did) and using a loop (while, for). It will most likely show you the answer on how this algorithm is built up.
Hint 1: You must know that Fibonnaci sequences are built up upon two initial values...
Hint 2: For when it comes to recursion, you should know how the function results are stored. That will explain your question as well.
They are not equvialent, and it certainly won't calculate the fibonanic sequence. Recursion can be thought of like a tree, so to compute Fib(8) say, by definition we take Fib(7) + Fib(6)
Fib(8)
/ \
Fib(7) Fib(6)
Which in turn require computing Fib(6), Fib(5), Fib(4) as follows:
Fib(8)
/ \
Fib(7) Fib(6)
/ \ / \
Fib(5) Fib(6) Fib(5) Fib(4)
And so on. What you are doing, would produce a different, depth 1 tree:
Fib(8)
/ \
7 6
Because, if you never call the function within the function, it can never go more deep. It should be clear from this and the other answers why it is not correct.
Related
I saw this code from the Stroustrup's book, but I can't understand how it works.
I just can't get how it increases by " 0, 1, 4, 9..."
int archaic_square(int v) {
int total = 0;
for (int i = 0; i < v; ++i) {
total += v;
}
return total;
}
int main() {
for (int i = 0; i < 100; ++i) {
cout << i << '\t' << archaic_square(i) << '\n';
}
return 0;
}
The code in archaic_square is starting total off as zero, then adding v to it v times (in the loop).
By definition, it will then end up as:
0 + v + v + … + v
\___________/
v times
which is 0 + v * v, or v2.
In more explicit detail:
adding zero to zero, zero times, gives you zero (0);
adding one to zero, once, gives you one (0, 1);
adding two to zero, two times, gives you four (0, 2, 4);
adding three to zero, three times, gives you nine (0, 3, 6, 9);
adding four to zero, four times, gives you sixteen (0, 4, 8, 12, 16);
and so on, ad infinitum.
Remember from arithmetic that multiplication is repeated just a addition (or rather a repeated addition by definition)? That's all that's happening here.
Since v is getting added v times, it is the same as v * v, or v squared.
That code calculates squares by the method of differences. It's an alternative way of evaluating functions, and has some benefits over the usual plug-in-the-values approach. It was used in Babbage's difference engine, which was designed in the early 1800s to calculate mathematical tables for logarithms, trig functions, etc. to 40 (!) digits.
The underlying idea is that you begin with a list of values that you know how to evaluate. You subtract each value from its neighboring value, giving you a list of first differences. Then you subtract each difference value from its neighboring difference value, giving you a list of second differences. Continue until you reach a level where all the difference values are equal. For a second-order polynomial (such as x^2) the second differences will all be equal. For a third-order polynomial, the third differences will all be equal. And so on.
So for calculating squares, you end up with this:
Value First Difference Second Difference
0
1 1
4 3 2
9 5 2
16 7 2
25 9 2
Now you can reverse the process. Start with a result of 0. Add the first difference (1), giving the next result (1). Then increase the first difference by the second difference (2), giving the next first difference (3); add that to the previous result (1), giving the next result (4). Then increase the new first difference (3) by the second difference (2), giving the next first difference (5); add that to the previous result (4), giving the next result (9). Continue until done.
So with only the first value (0), the first difference (1), and the (constant) second difference (2), we can generate as long a list of squares as we would like.
When you need a list of results, calculating them one after another like this replaces multiplication with addition, which, back in the olden days, was much faster. Further, if a computer (back when a computer was a person who did tedious calculations to produce mathematical) made a mistake, all the results after that mistake would be wrong, too, so the mathematician in charge of the project didn't have to provide for checking every result; spot checking was sufficient.
Calculating trig functions, of course, is a bit more tricky, because they aren't defined by polynomials. But over a small enough region they can be approximated by a polynomial.
Babbage's engine would have calculated 40 digit values, with up to 7 levels of differences. It mechanically went through the sequence of steps I mentioned above, grinding out results at a rate of one every few seconds. Babbage didn't actually build the full difference engine; he got an inspiration for a much more powerful "Analytical engine" which he also never built. It would have been a precursor to modern digital computers, with 1000 40-digit storage units, an arithmetic processor, and punched cards to control the sequence of operations.
Given a number N (<=10000), find the minimum number of primatic numbers which sum up to N.
A primatic number refers to a number which is either a prime number or can be expressed as power of prime number to itself i.e. prime^prime e.g. 4, 27, etc.
I tried to find all the primatic numbers using seive and then stored them in a vector (code below) but now I am can't see how to find the minimum of primatic numbers that sum to a given number.
Here's my sieve:
#include<algorithm>
#include<vector>
#define MAX 10000
typedef long long int ll;
ll modpow(ll a, ll n, ll temp) {
ll res=1, y=a;
while (n>0) {
if (n&1)
res=(res*y)%temp;
y=(y*y)%temp;
n/=2;
}
return res%temp;
}
int isprimeat[MAX+20];
std::vector<int> primeat;
//Finding all prime numbers till 10000
void seive()
{
ll i,j;
isprimeat[0]=1;
isprimeat[1]=1;
for (i=2; i<=MAX; i++) {
if (isprimeat[i]==0) {
for (j=i*i; j<=MAX; j+=i) {
isprimeat[j]=1;
}
}
}
for (i=2; i<=MAX; i++) {
if (isprimeat[i]==0) {
primeat.push_back(i);
}
}
isprimeat[4]=isprimeat[27]=isprimeat[3125]=0;
primeat.push_back(4);
primeat.push_back(27);
primeat.push_back(3125);
}
int main()
{
seive();
std::sort(primeat.begin(), primeat.end());
return 0;
}
One method could be to store all primatics less than or equal to N in a sorted list - call this list L - and recursively search for the shortest sequence. The easiest approach is "greedy": pick the largest spans / numbers as early as possible.
for N = 14 you'd have L = {2,3,4,5,7,8,9,11,13}, so you'd want to make an algorithm / process that tries these sequences:
13 is too small
13 + 13 -> 13 + 2 will be too large
11 is too small
11 + 11 -> 11 + 4 will be too large
11 + 3 is a match.
You can continue the process by making the search function recurse each time it needs another primatic in the sum, which you would aim to have occur a minimum number of times. To do so you can pick the largest -> smallest primatic in each position (the 1st, 2nd etc primatic in the sum), and include another number in the sum only if the primatics in the sum so far are small enough that an additional primatic won't go over N.
I'd have to make a working example to find a small enough N that doesn't result in just 2 numbers in the sum. Note that because you can express any natural number as the sum of at most 4 squares of natural numbers, and you have a more dense set L than the set of squares, so I'd think it rare you'd have a result of 3 or more for any N you'd want to compute by hand.
Dynamic Programming approach
I have to clarify that 'greedy' is not the same as 'dynamic programming', it can give sub-optimal results. This does have a DP solution though. Again, i won't write the final process in code but explain it as a point of reference to make a working DP solution from.
To do this we need to build up solutions from the bottom up. What you need is a structure that can store known solutions for all numbers up to some N, this list can be incrementally added to for larger N in an optimal way.
Consider that for any N, if it's primatic then the number of terms for N is just 1. This applies for N=2-5,7-9,11,13,16,17,19. The number of terms for all other N must be at least two, which means either it's a sum of two primatics or a sum of a primatic and some other N.
The first few examples that aren't trivial:
6 - can be either 2+4 or 3+3, all the terms here are themselves primatic so the minimum number of terms for 6 is 2.
10 - can be either 2+8, 3+7, 4+6 or 5+5. However 6 is not primatic, and taking that solution out leaves a minimum of 2 terms.
12 - can be either 2+10, 3+9, 4+8, 5+7 or 6+6. Of these 6+6 and 2+10 contain non-primatics while the others do not, so again 2 terms is the minimum.
14 - ditto, there exist two-primatic solutions: 3+11, 5+9, 7+7.
The structure for storing all of these solutions needs to be able to iterate across solutions of equal rank / number of terms. You already have a list of primatics, this is also the list of solutions that need only one term.
Sol[term_length] = list(numbers). You will also need a function / cache to look up some N's shortest-term-length, eg S(N) = term_length iif N in Sol[term_length]
Sol[1] = {2,3,4,5 ...} and Sol[2] = {6,10,12,14 ...} and so on for Sol[3] and onwards.
Any solution can be found using one term from Sol[1] that is primatic. Any solution requiring two primatics will be found in Sol[2]. Any solution requiring 3 will be in Sol[3] etc.
What you need to recognize here is that a number S(N) = 3 can be expressed Sol[1][a] + Sol[1][b] + Sol[1][c] for some a,b,c primatics, but it can also be expressed as Sol[1][a] + Sol[2][d], since all Sol[2] must be expressible as Sol[1][x] + Sol[1][y].
This algorithm will in effect search Sol[1] for a given N, then look in Sol[1] + Sol[K] with increasing K, but to do this you will need S and Sol structures roughly in the form shown here (or able to be accessed / queried in a similar manner).
Working Example
Using the above as a guideline I've put this together quickly, it even shows which multi-term sum it uses.
https://ideone.com/7mYXde
I can explain the code in-depth if you want but the real DP section is around lines 40-64. The recursion depth (also number of additional terms in the sum) is k, a simple dual-iterator while loop checks if a sum is possible using the kth known solutions and primatics, if it is then we're done and if not then check k+1 solutions, if any. Sol and S work as described.
The only confusing part might be the use of reverse iterators, it's just to make != end() checking consistent for the while condition (end is not a valid iterator position but begin is, so != begin would be written differently).
Edit - FYI, the first number that takes at least 3 terms is 959 - had to run my algorithm to 1000 numbers to find it. It's summed from 6 + 953 (primatic), no matter how you split 6 it's still 3 terms.
I am reading about recursive functions, and I have been trying to figure out the mathematical formula for this one. I thought maybe it was a logarithmic function, but that does not seem to be it. If anyone could point me in the right direction, I would appreciate it.
unsigned f(unsigned n){
if(n<2)
return 1;
return 1 + f(n/2);
}
It is a logarithm function, just to the base 2. More specifically, it's the ceil 1 + floor(log2(n)).
Since you are concerned about the math formula that the function is based on:
Here it is: f is a function of n:
f(n) = 1 + f(n/2) if n >=2
f(n) = 1 if n <=1 //n is unsigned so n >=0
With the above formula in mind, the underlying algorithm then have logarithmic complexity. The reason is that at every step, it will reduce the size of n by half and it will take log(n) (base 2) steps to reach 1, therefore, it is logarithmic complexity with O(logn).
I know you said "mathematical function" and that's set the perspective for existing answers, but to show another perspective, it returns:
number of bits needed to store n (if you consider 1 bit necessary to meaningfully encode number 0 distinct from no number at all)
1-based index of the most significant bit set in n|1, aka
minimum of (1) and (the 1-based index of the most significant bit set in n)
Sometimes when you're looking at code it can help to see that a function could be used with different intent - either perspective might make it easier to understand the context of use.
This function returns the number of times the function executes by dividing the whole number by 2 until reaching zero
EX:
f(8)-
return f(4) //continue on
return f(2) //continue on
return f(1) //we know this is 1
return f(2) //now we can do this one, 2
return f(4) //and this one, 3
More precisely, this implements floor(logbase2(n))
I am trying to write a C++ program that works like the game 24. For those who don't know how it is played, basically you try to find any way that 4 numbers can total 24 through the four algebraic operators of +, -, /, *, and parenthesis.
As an example, say someone inputs 2,3,1,5
((2+3)*5) - 1 = 24
It was relatively simple to code the function to determine if three numbers can make 24 because of the limited number of positions for parenthesis, but I can not figure how code it efficiently when four variables are entered.
I have some permutations working now but I still cannot enumerate all cases because I don't know how to code for the cases where the operations are the same.
Also, what is the easiest way to calculate the RPN? I came across many pages such as this one:
http://www.dreamincode.net/forums/index.php?showtopic=15406
but as a beginner, I am not sure how to implement it.
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
bool MakeSum(int num1, int num2, int num3, int num4)
{
vector<int> vi;
vi.push_back(num1);
vi.push_back(num2);
vi.push_back(num3);
vi.push_back(num4);
sort(vi.begin(),vi.end());
char a1 = '+';
char a2 = '-';
char a3 = '*';
char a4 = '/';
vector<char> va;
va.push_back(a1);
va.push_back(a2);
va.push_back(a3);
va.push_back(a4);
sort(va.begin(),va.end());
while(next_permutation(vi.begin(),vi.end()))
{
while(next_permutation(va.begin(),va.end()))
{
cout<<vi[0]<<vi[1]<<vi[2]<<vi[3]<< va[0]<<va[1]<<va[2]<<endl;
cout<<vi[0]<<vi[1]<<vi[2]<<va[0]<< vi[3]<<va[1]<<va[2]<<endl;
cout<<vi[0]<<vi[1]<<vi[2]<<va[0]<< va[1]<<vi[3]<<va[2]<<endl;
cout<<vi[0]<<vi[1]<<va[0]<<vi[2]<< vi[3]<<va[1]<<va[2]<<endl;
cout<<vi[0]<<vi[1]<<va[0]<<vi[2]<< va[1]<<vi[3]<<va[2]<<endl;
}
}
return 0;
}
int main()
{
MakeSum(5,7,2,1);
return 0;
}
So, the simple way is to permute through all possible combinations. This is slightly tricky, the order of the numbers can be important, and certainly the order of operations is.
One observation is that you are trying to generate all possible expression trees with certain properties. One property is that the tree will always have exactly 4 leaves. This means the tree will also always have exactly 3 internal nodes. There are only 3 possible shapes for such a tree:
A
/ \
N A
/ \ (and the mirror image)
N A
/ \
N N
A
/ \
N A
/ \
A N (and the mirror image)
/ \
N N
A
/` `\
A A
/ \ / \
N N N N
In each spot for A you can have any one of the 4 operations. In each spot for N you can have any one of the numbers. But each number can only appear for one N.
Coding this as a brute force search shouldn't be too hard, and I think that after you have things done this way it will become easier to think about optimizations.
For example, + and * are commutative. This means that mirrors that flip the left and right children of those operations will have no effect. It might be possible to cut down searching through all such flips.
Someone else mentioned RPN notation. The trees directly map to this. Here is a list of all possible trees in RPN:
N N N N A A A
N N N A N A A
N N N A A N A
N N A N N A A
N N A N A N A
That's 4*3*2 = 24 possibilities for numbers, 4*4*4 = 64 possibilities for operations, 24 * 64 * 5 = 7680 total possibilities for a given set of 4 numbers. Easily countable and can be evaluated in a tiny fraction of a second on a modern system. Heck, even in basic on my old Atari 8 bit I bet this problem would only take minutes for a given group of 4 numbers.
You can just use Reverse Polish Notation to generate the possible expressions, which should remove the need for parantheses.
An absolutely naive way to do this would be to generate all possible strings of 4 digits and 3 operators (paying no heed to validity as an RPN), assume it is in RPN and try to evaluate it. You will hit some error cases (as in invalid RPN strings). The total number of possibilities (if I calculated correctly) is ~50,000.
A more clever way should get it down to ~7500 I believe (64*24*5 to be exact): Generate a permutation of the digits (24 ways), generate a triplet of 3 operators (4^3 = 64 ways) and now place the operators among the digits to make it valid RPN(there are 5 ways, see Omnifarious' answer).
You should be able to find permutation generators and RPN calculators easily on the web.
Hope that helps!
PS: Just FYI: RPN is nothing but the postorder traversal of the corresponding expression tree, and for d digits, the number is d! * 4^(d-1) * Choose(2(d-1), (d-1))/d. (The last term is a catalan number).
Edited: The solution below is wrong. We also need to consider the numbers makeable with just x_2 and x_4, and with just x_1 and x_4. This approach can still work, but it's going to be rather more complex (and even less efficient). Sorry...
Suppose we have four numbers x_1, x_2, x_3, x_4. Write
S = { all numbers we can make just using x_3, x_4 },
Then we can rewrite the set we're interested in, which I'll call
T = { all numbers we can make using x_1, x_2, x_3, x_4 }
as
T = { all numbers we can make using x_1, x_2 and some s from S }.
So an algorithm is to generate all possible numbers in S, then use each number s in S in turn to generate part of T. (This will generalise fairly easily to n numbers instead of just 4).
Here's a rough, untested code example:
#include <set> // we can use std::set to store integers without duplication
#include <vector> // we might want duplication in the inputs
// the 2-number special case
std::set<int> all_combinations_from_pair(int a, int b)
{
std::set results;
// here we just use brute force
results.insert(a+b); // = b+a
results.insert(a-b);
results.insert(b-a);
results.insert(a*b); // = b*a
// need to make sure it divides exactly
if (a%b==0) results.insert(a/b);
if (b%a==0) results.insert(b/a);
return results;
}
// the general case
std::set<int> all_combinations_from(std::vector<int> inputs)
{
if (inputs.size() == 2)
{
return all_combinations_from_pair(inputs[0], inputs[1]);
}
else
{
std::set<int> S = all_combinations_from_pair(inputs[0], inputs[1]);
std::set<int> T;
std::set<int> rest = S;
rest.remove(rest.begin());
rest.remove(rest.begin()); // gets rid of first two
for (std::set<int>.iterator i = S.begin(); i < S.end(); i++)
{
std::set<int> new_inputs = S;
new_inputs.insert(*i);
std::set<int> new_outputs = all_combinations_from(new_inputs);
for (std::set<int>.iterator j = new_outputs.begin(); j < new_outputs.end(); j++)
T.insert(*j); // I'm sure you can do this with set_union()
}
return T;
}
}
If you are allowed to use the same operator twice, you probably don't want to mix the operators into the numbers. Instead, perhaps use three 0's as a placeholder for where operations will occur (none of the 4 numbers are 0, right?) and use another structure to determine which operations will be used.
The second structure could be a vector<int> initialized with three 1's followed by three 0's. The 0's correspond to the 0's in the number vector. If a 0 is preceded by zero 1's, the corresponding operation is +, if preceded by one 1, it's -, etc. For example:
6807900 <= equation of form ( 6 # 8 ) # ( 7 # 9 )
100110 <= replace #'s with (-,-,/)
possibility is (6-8)-(7/9)
Advance through the operation possibilities using next_permutation in an inner loop.
By the way, you can also return early if the number-permutation is an invalid postfix expression. All permutations of the above example less than 6708090 are invalid, and all greater are valid, so you could start with 9876000 and work your way down with prev_permutation.
Look up the Knapsack problem (here's a link to get you started: http://en.wikipedia.org/wiki/Knapsack_problem), this problem is pretty close to that, just a little harder (and the Knapsack problem is NP-complete!)
One thing that might make this faster than normal is parallelisation. Check out OpenMP. Using this, more than one check is carried out at once (your "alg" function) thus if you have a dual/quad core cpu, your program should be faster.
That said, if as suggested above the problem is NP-complete, it'll be faster, not necessarily fast.
i wrote something like this before. You need a recursive evaluator. Call evaluate, when you hit "(" call evaluate again otherwise run along with digits and operators till you hit ")", now return the result of the -+*/ operations the the evaluate instance above you
An algorithm which will take two positive numbers N and K and calculate the biggest possible number we can get by transforming N into another number via removing K digits from N.
For ex, let say we have N=12345 and K=3 so the biggest possible number we can get by removing 3 digits from N is 45 (other transformations would be 12, 15, 35 but 45 is the biggest). Also you cannot change the order of the digits in N (so 54 is NOT a solution). Another example would be N=66621542 and K=3 so the solution will be 66654.
I know this is a dynamic programming related problem and I can't get any idea about solving it. I need to solve this for 2 days, so any help is appreciated. If you don't want to solve this for me you don't have to but please point me to the trick or at least some materials where i can read up more about some similar issues.
Thank you in advance.
This can be solved in O(L) where L = number of digits. Why use complicated DP formulas when we can use a stack to do this:
For: 66621542
Add a digit on the stack while there are less than or equal to L - K digits on the stack:
66621. Now, remove digits from the stack while they are less than the currently read digit and put the current digit on the stack:
read 5: 5 > 2, pop 1 off the stack. 5 > 2, pop 2 also. put 5: 6665
read 4: stack isnt full, put 4: 66654
read 2: 2 < 4, do nothing.
You need one more condition: be sure not to pop off more items from the stack than there are digits left in your number, otherwise your solution will be incomplete!
Another example: 12345
L = 5, K = 3
put L - K = 2 digits on the stack: 12
read 3, 3 > 2, pop 2, 3 > 1, pop 1, put 3. stack: 3
read 4, 4 > 3, pop 3, put 4: 4
read 5: 5 > 4, but we can't pop 4, otherwise we won't have enough digits left. so push 5: 45.
Well, to solve any dynamic programming problem, you need to break it down into recurring subsolutions.
Say we define your problem as A(n, k), which returns the largest number possible by removing k digits from n.
We can define a simple recursive algorithm from this.
Using your example, A(12345, 3) = max { A(2345, 2), A(1345, 2), A(1245, 2), A(1234, 2) }
More generally, A(n, k) = max { A(n with 1 digit removed, k - 1) }
And you base case is A(n, 0) = n.
Using this approach, you can create a table that caches the values of n and k.
int A(int n, int k)
{
typedef std::pair<int, int> input;
static std::map<input, int> cache;
if (k == 0) return n;
input i(n, k);
if (cache.find(i) != cache.end())
return cache[i];
cache[i] = /* ... as above ... */
return cache[i];
}
Now, that's the straight forward solution, but there is a better solution that works with a very small one-dimensional cache. Consider rephrasing the question like this: "Given a string n and integer k, find the lexicographically greatest subsequence in n of length k". This is essentially what your problem is, and the solution is much more simple.
We can now define a different function B(i, j), which gives the largest lexicographical sequence of length (i - j), using only the first i digits of n (in other words, having removed j digits from the first i digits of n).
Using your example again, we would have:
B(1, 0) = 1
B(2, 0) = 12
B(3, 0) = 123
B(3, 1) = 23
B(3, 2) = 3
etc.
With a little bit of thinking, we can find the recurrence relation:
B(i, j) = max( 10B(i-1, j) + ni , B(i-1, j-1) )
or, if j = i then B(i, j) = B(i-1, j-1)
and B(0, 0) = 0
And you can code that up in a very similar way to the above.
The trick to solving a dynamic programming problem is usually to figuring out what the structure of a solution looks like, and more specifically if it exhibits optimal substructure.
In this case, it seems to me that the optimal solution with N=12345 and K=3 would have an optimal solution to N=12345 and K=2 as part of the solution. If you can convince yourself that this holds, then you should be able to express a solution to the problem recursively. Then either implement this with memoisation or bottom-up.
The two most important elements of any dynamic programming solution are:
Defining the right subproblems
Defining a recurrence relation between the answer to a sub-problem and the answer to smaller sub-problems
Finding base cases, the smallest sub-problems whose answer does not depend on any other answers
Figuring out the scan order in which you must solve the sub-problems (so that you never use the recurrence relation based on uninitialized data)
You'll know that you have the right subproblems defined when
The problem you need the answer to is one of them
The base cases really are trivial
The recurrence is easy to evaluate
The scan order is straightforward
In your case, it is straightforward to specify the subproblems. Since this is probably homework, I will just give you the hint that you might wish that N had fewer digits to start off with.
Here's what i think:
Consider the first k + 1 digits from the left. Look for the biggest one, find it and remove the numbers to the left. If there exists two of the same biggest number, find the leftmost one and remove the numbers to the left of that. store the number of removed digits ( name it j ).
Do the same thing with the new number as N and k+1-j as K. Do this until k+1 -j equals to 1 (hopefully, it will, if i'm not mistaken).
The number you end up with will be the number you're looking for.