I have found this algorithm for a program that returns number of possible triangles with a specific perimeter. I know that this problem has some other ways but using them I get time limit error for large numbers maybe because they are not optimized. This one seems much more optimized but I get stack overflow for recursion for large inputs. Can anyone help me optimize it by converting it into tail recursion or do something else with this problem?
Here is my found algorithm:
#include<iostream>
using namespace std;
int foo(int);
int main(){
int n;
cin>>n;
cout<<foo(n);
return 0;
}
int foo(int n){
int p=n/2-2;
int t;
if(p%6<5&&p%6>0)
t=(p+6-p%6)/6;
else if(p%6==5)
t=(p+6-p%6)/6+1;
else
t=p/6;
if(n==3||n==5||n==6)
return 1;
else if(n==4)
return 0;
else{
if(n%2==0)
return foo(n-3);
else
return foo(n+1)+t;
}
}
Problem is here:
return foo(n+1) + t;
// ^^^
The addition makes the recursive call to foo
not being the very last thing that happens in the function. So you need to move that addition into the function:
return foo(n + 1, t);
To make the other recursive call compatible, you'll just provide addition's neutral element:
return foo(n-3, 0);
New function's signature:
int foo(int n, int offset = 0); // default parameter allows for calling just as before.
Final question remaining: Where to do the addition???
Well, two places are obvious:
if(n==3||n==5||n==6)
return 1 + offset;
else if(n==4)
return /*0 +*/ offset;
offset
is the accumulated offset of all previous recursive calls! So you will have to add it to whatever you add to next function's recursive call, so:
// ...
else if(n % 2 == 0)
return foo(n-3, /*0 +*/ offset);
else
return foo(n+1, t + offset);
Side note: Your algorithm doesn't seem to be suitable for negative numbers. Calling (unmodified) foo
with these ends up in recurring until stack overflow (if stack was large enough, would possibly result in undefined behaviour due to signed integer overflow). If you don't intend to use it on negative numbers you absolutely should use unsigned int
as data type! Otherwise, it needs appropriate fixing.
The values 0, 1 and 2 need special treatment, too, they lead to negative n
(for unmodified foo
, see above). But you could do that almost trivially:
if(n == 4)
{ ... }
else if (n < 6)
{ ... }
That would return 1 for n being 0, 1 or 2 (even with tail call optimised version, as offset would be still 0) and even spare some comparisons. If you need to return 0 for the special values: Simple, too:
else if (n < 6)
return (n >= 3) + offset;
You could even merge all into one single condition:
if(n < 6)
return (n == 3 || n > 4) + offset; // returning 0 for 0, 1, 2
return (n <= 3 || n > 4) + offset; // returning 1 for 0, 1, 2
Greatest advantage: during all those recursions, you need only one single check, whereas all others are done only when stop condition is met (ie just once).
And you can move calculation of p
and t
behind the stop condition(s), you don't use them there anyway...
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.