I want to have it add every once a parameter it has not yet found to the map, but when I try numbers higher than 47 it gives me negative numbers, clearly impossible
#include <map>
using namespace std;
//memoization
map<unsigned int, unsigned int> memo;
map<unsigned int, unsigned int>::iterator it;
int fibonacci(int n)
{
it = memo.find(n);
if (it != memo.end())
{
cout << it->first<<endl;
return memo.at(n);
}
if (n <= 2)
{
return 1;
}
memo.insert({ n, fibonacci(n - 1) + fibonacci(n - 2) });
cout << "----"<<n<<endl;
return memo.at(n);
}
int main()
{
cout<<fibonacci(48);
}
First, let's take care of the negative numbers. Comments above explained that you have an overflow of the 32-bit int.
However, you could stretch your code a bit further would you not convert an unsigned int you calculate to a signed one you return from that function.
The solution is to use larger types, like unsigned long long int, AKA uint64_t
UPDATE
There are a few things sub-optimal in accepted answer.
As I noted in a comment there, the code is searching the map twice: it = memo.find(n); and memo[n];; should just return it->second;
There is no need for the key in that map to be 64-bit wide; 32 bit is enough to overflow 64-bit Fibonacci.
As the order of map keys is not important (you only do insert / lookup), the unordered_map will perform better (constant time instead of logarithmic).
You may notice that the memo is filled in order, and accessed by the index. Much better container here will be a vector with a "free" insert and lookup.
The calling function should NOT fill in memo, as it's not its business.
Here is my version:
#include <vector>
#include <iostream>
//memoization
static std::vector<uint64_t> memo = { 0, 0, 1 };
uint64_t fibonacci(unsigned int n) {
if (n < memo.size())
return memo[n];
memo.push_back(fibonacci(n - 1) + fibonacci(n - 2));
return memo[n];
}
int main() {
std::cout << fibonacci(32'000);
}
but when I try numbers higher than 47 it gives me negative numbers,
clearly impossible
unsigned int size is {0 to 4,294,967,295}, 48th Fibonacci is 4,807,526,976
Fixed code
#include <map>
#include <iostream>
#include <cstdint>
using namespace std;
//memoization
map<int64_t , int64_t> memo;
map<int64_t , int64_t>::iterator it;
int64_t fibonacci(int64_t n) {
it = memo.find(n);
if (it != memo.end()) {
return it->second;;
}
memo.insert({n, fibonacci(n - 1) + fibonacci(n - 2)});
return memo[n];
}
int main() {
memo.insert({0, 0});
memo.insert({1, 1});
memo.insert({2, 1});
cout << fibonacci(50);
}
Related
I have a Time limit exceeded issue in problem 100 from UVa.
the question is here:
https://onlinejudge.org/index.php?option=com_onlinejudge&Itemid=8&category=24&page=show_problem&problem=36
Here is my code. Please help me find a solution. How can I avoid such problems?
I don't know if it is the problem with cin and cout or the while loops? this program works well in my terminal when I run it.
#include <iostream>
using namespace std;
int main()
{
int i , j, temp, n;
while (cin >> i >> j) //asking for user input
{
int x, y;
x = i;
y = j;
if (i > j) //sorting i and j to fix the order of numbers
{
temp = j;
j = i;
i = temp;
}
int answer = 0;
int counter;
while (i <= j)
{
n = i;
counter = 1; // make the value of counter to 1 because it increases if i is 1
while (1)
{
if(n == 1) { //if n = 1 then stop
break;
} else if (n % 2 == 0) //cheak if i is odd
{
n = (3 % n) + 1;
} else {
n = n / 2; //cheak if i is even
}
counter++; //increase by one for every number that is not 1
}
if (counter > answer)
{
answer = counter;
}
i++;
}
cout << x << " " << y << " " << answer << "\n";
}
return 0;
}
Thanks in advance
In my humble opinion this problem is not about calculating the resulting values using the given algorithm. Because of the simplicity this is just some noise. So,maybe we are talking about a XY Problem here.
Maybe I am wrong, but the main problem here seems to be memoization.
It maybe that values need to be calculated over and over again, because they are in some overlapped range. And this is not necessary.
So, we could memorize already calculated values, for example in a std::unordered_map (or std::map). So, something like in the below:
unsigned int getSteps(size_t index) noexcept {
unsigned counter{};
while (index != 1) {
if (index % 2) index = index * 3 + 1;
else index /= 2;
++counter;
}
return counter+1;
}
unsigned int getStepsMemo(size_t index) {
// Here we will memorize whatever we calculated before
static std::unordered_map<unsigned int, unsigned int> memo{};
// Resulting value
unsigned int result{};
// Look, if we did calculate the value in the past
auto iter = memo.find(index);
if (iter != memo.end())
// If yes, then reuse old value
result = iter->second;
else {
// If no, then calculate new and memorize it
result = getSteps(index);
memo[index] = result;
}
return result;
}
This will help with many given input pairs. It will avoid recalculating steps for already calculated values.
But having thought in this direction, we can also calculate all values at compile time and store them in a constexpr std::array. Then no calculation will be done during runtime. All steps for any number up to 10000 will be precalculated. So, the algorithm will never be called during runtime.
It should be clear that this is the fastest possible algorithm, because we do nothing. Just get the value from a lookup table.
And if we want to make things nice, then we pack everything in a class and let the class encapsulate the problem. Even input and output operatores will be overwritten and used for our own purposes.
And in the end, we will have an ultra fast one liner in our function main. Please see:
#include <iostream>
#include <utility>
#include <sstream>
#include <array>
#include <algorithm>
#include <iterator>
#include <unordered_map>
// All done during compile time -------------------------------------------------------------------
constexpr unsigned int getSteps(size_t index) noexcept {
unsigned counter{};
while (index != 1) {
if (index % 2) index = index * 3 + 1;
else index /= 2;
++counter;
}
return counter+1;
}
// Some helper to create a constexpr std::array initilized by a generator function
template <typename Generator, size_t ... Indices>
constexpr auto generateArrayHelper(Generator generator, std::index_sequence<Indices...>) {
return std::array<decltype(std::declval<Generator>()(size_t{})), sizeof...(Indices) > { generator(Indices+1)... };
}
template <size_t Size, typename Generator>
constexpr auto generateArray(Generator generator) {
return generateArrayHelper(generator, std::make_index_sequence<Size>());
}
constexpr size_t MaxIndex = 10000;
// This is the definition of a std::array<unsigned long long, 10000> with all step counts
constexpr auto steps = generateArray<MaxIndex>(getSteps);
// End of: All done during compile time -----------------------------------------------------------
// Some very simple helper class for easier handling of the functionality
struct StepsForPair {
// A pair with special functionality
unsigned int first{};
unsigned int second{};
// Simple extraction operator. Read 2 values
friend std::istream& operator >> (std::istream& is, StepsForPair& sfp) {
return is >> sfp.first >> sfp.second;
}
// Simple inserter. Sort first and second value and show result
friend std::ostream& operator << (std::ostream& os, const StepsForPair& sfp) {
unsigned int f{ sfp.first }, s{ sfp.second };
if (f > s) std::swap(f, s);
return os << sfp.first << ' ' << sfp.second << ' ' << *std::max_element(&steps[f], &steps[s]);
}
};
// Some test data. I will not use std::cin, but read from this std::istringstream here
std::istringstream testData{ R"(1 10
100 200
201 210
900 1000
22 22)" };
int main() {
// Read all input data and generate output
std::copy(std::istream_iterator<StepsForPair>(testData), {}, std::ostream_iterator<StepsForPair>(std::cout,"\n"));
}
Please note, since I do not have std::cin here on SO, I read the test values from a std::istringstream. Because of the overwritten extractor operator, this is easily possible.
If you want to read from std::cin then please replace in the std::copy statement in main "testData" eith "std::cin".
If you want to read from a file, then put a fileStream variable in there.
In this line n = (3 % n) + 1;, (3 % n) means that you take the remainder of 3 divided by n, which is probably not what you want. Change that to 3 * n
Ok so, I'm doing hackerrank's Fibonacci modified question. I am able to solve this question only when the iteration goes to 8 anything pass that and it starts returning a large negative number. I thought this was because of an integer overflow so I changed my types to unsigned long long yet the problems still persist. Any help is appreciated.
Link to original problem: https://www.hackerrank.com/challenges/fibonacci-modified/problem
#include <iostream>
#include <vector>
using namespace std;
int modFib(unsigned t1, unsigned t2, unsigned n) {
if (n == 1) {
return t1;
}
else if (n == 2) {
return t2;
} else {
return modFib(t1, t2, n-2) + (modFib(t1, t2, n-1) * modFib(t1, t2, n-1));
}
}
int main() {
cout << modFib(0, 1, 10) << endl;
return 0;
}
//Expected output is 84266613096281243382112
//I get -1022889632
In C++, the general range of an unsigned int is 0 to 4,294,967,295, so using an unsigned int will not be appropriate for this problem.
The expected output is actually larger than the maximum possible value of even an unsigned long long int, which goes from 0 to 18,446,744,073,709,551,615. This means that you cannot use either of these data types for this problem.
For such large values, you should look into the usage of BigNums.
I'm struggling a bit with dynamic programming. To be more specific, implementing an algorithm for finding Fibonacci numbers of n.
I have a naive algorithm that works:
int fib(int n) {
if(n <= 1)
return n;
return fib(n-1) + fib(n-2);
}
But when i try to do it with memoization the function always returns 0:
int fib_mem(int n) {
if(lookup_table[n] == NIL) {
if(n <= 1)
lookup_table[n] = n;
else
lookup_table[n] = fib_mem(n-1) + fib_mem(n-2);
}
return lookup_table[n];
}
I've defined the lookup_table and initially stored NIL in all elements.
Any ideas what could be wrong?
Here's the whole program as requested:
#include <iostream>
#define NIL -1
#define MAX 100
long int lookup_table[MAX];
using namespace std;
int fib(int n);
int fib_mem(int n);
void initialize() {
for(int i = 0; i < MAX; i++) {
lookup_table[i] == NIL;
}
}
int main() {
int n;
long int fibonnaci, fibonacci_mem;
cin >> n;
// naive solution
fibonnaci = fib(n);
// memoized solution
initialize();
fibonacci_mem = fib_mem(n);
cout << fibonnaci << endl << fibonacci_mem << endl;
return 0;
}
int fib(int n) {
if(n <= 1)
return n;
return fib(n-1) + fib(n-2);
}
int fib_mem(int n) {
if(lookup_table[n] == NIL) {
if(n <= 1)
lookup_table[n] = n;
else
lookup_table[n] = fib_mem(n-1) + fib_mem(n-2);
}
return lookup_table[n];
}
I tend to find the easiest way to write memoization by mixing the naive implementation with the memoization:
int fib_mem(int n);
int fib(int n) { return n <= 1 ? n : fib_mem(n-1) + fib_mem(n-2); }
int fib_mem(int n)
{
if (lookup_table[n] == NIL) {
lookup_table[n] = fib(n);
}
return lookup_table[n];
}
#include <iostream>
#define N 100
using namespace std;
const int NIL = -1;
int lookup_table[N];
void init()
{
for(int i=0; i<N; i++)
lookup_table[i] = NIL;
}
int fib_mem(int n) {
if(lookup_table[n] == NIL) {
if(n <= 1)
lookup_table[n] = n;
else
lookup_table[n] = fib_mem(n-1) + fib_mem(n-2);
}
return lookup_table[n];
}
int main()
{
init();
cout<<fib_mem(5);
cout<<fib_mem(7);
}
Using the exactly same function, and this is working fine.
You have done something wrong in initialisation of lookup_table.
Since the issue is initialization, the C++ standard library allows you to initialize sequences without having to write for loops and thus will prevent you from making mistakes such as using == instead of =.
The std::fill_n function does this:
#include <algorithm>
//...
void initialize()
{
std::fill_n(lookup_table, MAX, NIL);
}
Interesting concept. Speeding up by memoization.
There is a different concept. You could call it compile time memoization. But in reality it is a compile time pre calculation of all Fibonacci numbers that fit into a 64 bit value.
One important property of the Fibonacci series is that the values grow strongly exponential. So, all existing build in integer data types will overflow rather quick.
With Binet's formula you can calculate that the 93rd Fibonacci number is the last that will fit in a 64bit unsigned value.
And calculating 93 values during compilation is a really simple task.
We will first define the default approach for calculation a Fibonacci number as a constexpr function:
// Constexpr function to calculate the nth Fibonacci number
constexpr unsigned long long getFibonacciNumber(size_t index) noexcept {
// Initialize first two even numbers
unsigned long long f1{ 0 }, f2{ 1 };
// calculating Fibonacci value
while (index--) {
// get next value of Fibonacci sequence
unsigned long long f3 = f2 + f1;
// Move to next number
f1 = f2;
f2 = f3;
}
return f2;
}
With that, Fibonacci numbers can easily be calculated at runtime. Then, we fill a std::array with all Fibonacci numbers. We use also a constexpr and make it a template with a variadic parameter pack.
We use std::integer_sequence to create a Fibonacci number for indices 0,1,2,3,4,5, ....
That is straigtforward and not complicated:
template <size_t... ManyIndices>
constexpr auto generateArrayHelper(std::integer_sequence<size_t, ManyIndices...>) noexcept {
return std::array<unsigned long long, sizeof...(ManyIndices)>{ { getFibonacciNumber(ManyIndices)... } };
};
This function will be fed with an integer sequence 0,1,2,3,4,... and return a std::array<unsigned long long, ...> with the corresponding Fibonacci numbers.
We know that we can store maximum 93 values. And therefore we make a next function, that will call the above with the integer sequence 1,2,3,4,...,92,93, like so:
constexpr auto generateArray() noexcept {
return generateArrayHelper(std::make_integer_sequence<size_t, MaxIndexFor64BitValue>());
}
And now, finally,
constexpr auto FIB = generateArray();
will give us a compile-time std::array<unsigned long long, 93> with the name FIB containing all Fibonacci numbers. And if we need the i'th Fibonacci number, then we can simply write FIB[i]. There will be no calculation at runtime.
I do not think that there is a faster way to calculate the n'th Fibonacci number.
Please see the complete program below:
#include <iostream>
#include <array>
#include <utility>
// ----------------------------------------------------------------------
// All the following will be done during compile time
// Constexpr function to calculate the nth Fibonacci number
constexpr unsigned long long getFibonacciNumber(size_t index) {
// Initialize first two even numbers
unsigned long long f1{ 0 }, f2{ 1 };
// calculating Fibonacci value
while (index--) {
// get next value of Fibonacci sequence
unsigned long long f3 = f2 + f1;
// Move to next number
f1 = f2;
f2 = f3;
}
return f2;
}
// We will automatically build an array of Fibonacci numberscompile time
// Generate a std::array with n elements
template <size_t... ManyIndices>
constexpr auto generateArrayHelper(std::integer_sequence<size_t, ManyIndices...>) noexcept {
return std::array<unsigned long long, sizeof...(ManyIndices)>{ { getFibonacciNumber(ManyIndices)... } };
};
// Max index for Fibonaccis that for in an 64bit unsigned value (Binets formula)
constexpr size_t MaxIndexFor64BitValue = 93;
// Generate the required number of elements
constexpr auto generateArray()noexcept {
return generateArrayHelper(std::make_integer_sequence<size_t, MaxIndexFor64BitValue>());
}
// This is an constexpr array of all Fibonacci numbers
constexpr auto FIB = generateArray();
// ----------------------------------------------------------------------
// Test
int main() {
// Print all possible Fibonacci numbers
for (size_t i{}; i < MaxIndexFor64BitValue; ++i)
std::cout << i << "\t--> " << FIB[i] << '\n';
return 0;
}
Developed and tested with Microsoft Visual Studio Community 2019, Version 16.8.2.
Additionally compiled and tested with clang11.0 and gcc10.2
Language: C++17
There's a mistake in your initialize() function:
void initialize() {
for(int i = 0; i < MAX; i++) {
lookup_table[i] == NIL; // <- mistake
}
}
In the line pointed you compare lookup_table[i] and NIL (and don't use the result) instead of assigning NIL to lookup_table[i].
For assignment, you should use = instead of ==.
Also, in such situations the most right thing to do is compilation of your program with all warnings enabled. For example, MS VC++ shows the following warning:
warning C4553: '==': operator has no effect; did you intend '='?
The error is on initialize function (you've used comparison operator '==' where you want a attribution operator '='). But, on semantics, you don't need initialize look_table with -1 (NIL) because Fibonacci results never will be 0 (zero); so, you can initialize it all with zero.
Look below the final solution:
#include <iostream>
#define NIL 0
#define MAX 1000
long int lookup_table[MAX] = {};
using namespace std;
long int fib(int n) {
if(n <= 1)
return n;
return fib(n-1) + fib(n-2);
}
long int fib_mem(int n) {
assert(n < MAX);
if(lookup_table[n] == NIL) {
if(n <= 1)
lookup_table[n] = n;
else
lookup_table[n] = fib_mem(n-1) + fib_mem(n-2);
}
return lookup_table[n];
}
int main() {
int n;
long int fibonnaci, fibonacci_mem;
cout << " n = "; cin >> n;
// naive solution
fibonnaci = fib(n);
// memoized solution
// initialize();
fibonacci_mem = fib_mem(n);
cout << fibonnaci << endl << fibonacci_mem << endl;
return 0;
}
I'm a programming student, and for a project I'm working on, on of the things I have to do is compute the median value of a vector of int values and must be done by passing it through functions. Also the vector is initially generated randomly using the C++ random generator mt19937 which i have already written down in my code.I'm to do this using the sort function and vector member functions such as .begin(), .end(), and .size().
I'm supposed to make sure I find the median value of the vector and then output it
And I'm Stuck, below I have included my attempt. So where am I going wrong? I would appreciate if you would be willing to give me some pointers or resources to get going in the right direction.
Code:
#include<iostream>
#include<vector>
#include<cstdlib>
#include<ctime>
#include<random>
#include<vector>
#include<cstdlib>
#include<ctime>
#include<random>
using namespace std;
double find_median(vector<double>);
double find_median(vector<double> len)
{
{
int i;
double temp;
int n=len.size();
int mid;
double median;
bool swap;
do
{
swap = false;
for (i = 0; i< len.size()-1; i++)
{
if (len[i] > len[i + 1])
{
temp = len[i];
len[i] = len[i + 1];
len[i + 1] = temp;
swap = true;
}
}
}
while (swap);
for (i=0; i<len.size(); i++)
{
if (len[i]>len[i+1])
{
temp=len[i];
len[i]=len[i+1];
len[i+1]=temp;
}
mid=len.size()/2;
if (mid%2==0)
{
median= len[i]+len[i+1];
}
else
{
median= (len[i]+0.5);
}
}
return median;
}
}
int main()
{
int n,i;
cout<<"Input the vector size: "<<endl;
cin>>n;
vector <double> foo(n);
mt19937 rand_generator;
rand_generator.seed(time(0));
uniform_real_distribution<double> rand_distribution(0,0.8);
cout<<"original vector: "<<" ";
for (i=0; i<n; i++)
{
double rand_num=rand_distribution(rand_generator);
foo[i]=rand_num;
cout<<foo[i]<<" ";
}
double median;
median=find_median(foo);
cout<<endl;
cout<<"The median of the vector is: "<<" ";
cout<<median<<endl;
}
The median is given by
const auto median_it = len.begin() + len.size() / 2;
std::nth_element(len.begin(), median_it , len.end());
auto median = *median_it;
For even numbers (size of vector) you need to be a bit more precise. E.g., you can use
assert(!len.empty());
if (len.size() % 2 == 0) {
const auto median_it1 = len.begin() + len.size() / 2 - 1;
const auto median_it2 = len.begin() + len.size() / 2;
std::nth_element(len.begin(), median_it1 , len.end());
const auto e1 = *median_it1;
std::nth_element(len.begin(), median_it2 , len.end());
const auto e2 = *median_it2;
return (e1 + e2) / 2;
} else {
const auto median_it = len.begin() + len.size() / 2;
std::nth_element(len.begin(), median_it , len.end());
return *median_it;
}
There are of course many different ways how we can get element e1. We could also use max or whatever we want. But this line is important because nth_element only places the nth element correctly, the remaining elements are ordered before or after this element, depending on whether they are larger or smaller. This range is unsorted.
This code is guaranteed to have linear complexity on average, i.e., O(N), therefore it is asymptotically better than sort, which is O(N log N).
Regarding your code:
for (i=0; i<len.size(); i++){
if (len[i]>len[i+1])
This will not work, as you access len[len.size()] in the last iteration which does not exist.
std::sort(len.begin(), len.end());
double median = len[len.size() / 2];
will do it. You might need to take the average of the middle two elements if size() is even, depending on your requirements:
0.5 * (len[len.size() / 2 - 1] + len[len.size() / 2]);
Instead of trying to do everything at once, you should start with simple test cases and work upwards:
#include<vector>
double find_median(std::vector<double> len);
// Return the number of failures - shell interprets 0 as 'success',
// which suits us perfectly.
int main()
{
return find_median({0, 1, 1, 2}) != 1;
}
This already fails with your code (even after fixing i to be an unsigned type), so you could start debugging (even 'dry' debugging, where you trace the code through on paper; that's probably enough here).
I do note that with a smaller test case, such as {0, 1, 2}, I get a crash rather than merely failing the test, so there's something that really needs to be fixed.
Let's replace the implementation with one based on overseas's answer:
#include <algorithm>
#include <limits>
#include <vector>
double find_median(std::vector<double> len)
{
if (len.size() < 1)
return std::numeric_limits<double>::signaling_NaN();
const auto alpha = len.begin();
const auto omega = len.end();
// Find the two middle positions (they will be the same if size is odd)
const auto i1 = alpha + (len.size()-1) / 2;
const auto i2 = alpha + len.size() / 2;
// Partial sort to place the correct elements at those indexes (it's okay to modify the vector,
// as we've been given a copy; otherwise, we could use std::partial_sort_copy to populate a
// temporary vector).
std::nth_element(alpha, i1, omega);
std::nth_element(i1, i2, omega);
return 0.5 * (*i1 + *i2);
}
Now, our test passes. We can write a helper method to allow us to create more tests:
#include <iostream>
bool test_median(const std::vector<double>& v, double expected)
{
auto actual = find_median(v);
if (abs(expected - actual) > 0.01) {
std::cerr << actual << " - expected " << expected << std::endl;
return true;
} else {
std::cout << actual << std::endl;
return false;
}
}
int main()
{
return test_median({0, 1, 1, 2}, 1)
+ test_median({5}, 5)
+ test_median({5, 5, 5, 0, 0, 0, 1, 2}, 1.5);
}
Once you have the simple test cases working, you can manage more complex ones. Only then is it time to create a large array of random values to see how well it scales:
#include <ctime>
#include <functional>
#include <random>
int main(int argc, char **argv)
{
std::vector<double> foo;
const int n = argc > 1 ? std::stoi(argv[1]) : 10;
foo.reserve(n);
std::mt19937 rand_generator(std::time(0));
std::uniform_real_distribution<double> rand_distribution(0,0.8);
std::generate_n(std::back_inserter(foo), n, std::bind(rand_distribution, rand_generator));
std::cout << "Vector:";
for (auto v: foo)
std::cout << ' ' << v;
std::cout << "\nMedian = " << find_median(foo) << std::endl;
}
(I've taken the number of elements as a command-line argument; that's more convenient in my build than reading it from cin). Notice that instead of allocating n doubles in the vector, we simply reserve capacity for them, but don't create any until needed.
For fun and kicks, we can now make find_median() generic. I'll leave that as an exercise; I suggest you start with:
typename<class Iterator>
auto find_median(Iterator alpha, Iterator omega)
{
using value_type = typename Iterator::value_type;
if (alpha == omega)
return std::numeric_limits<value_type>::signaling_NaN();
}
I'm getting Segmentation error for the code below. This is a solution to the SPOJ problem "Coins".
I went through How to avoid SIGSEGV? and I made sure not to use uninitialized pointers, not to access out of memory etc (given n ≤ 109).
I know that an array a[1000000000] would lead to stack overflow, so I used std::map. Will a std::map ever lead to a stack overflow? What is wrong with my code?
#include <iostream>
#include <cstring>
#include <algorithm>
#include <cstdio>
#include <cmath>
#include <map>
using namespace std;
map<unsigned long long int, unsigned long long int> a;
unsigned long long int dp(unsigned long long int n)
{
if (a.find(n) == a.end())
a[n] = dp(n/2) + dp(n/3) + dp(n/4);
return a[n];
}
int main()
{
for (unsigned long long int i = 1; i <= 24; i++) {
a[i] = i;
if (i == 12 || i == 24)
a[i] = i + 1;
}
unsigned long long int n = 0;
cin >> n;
while (!feof(stdin)) {
printf("%llu\n", dp(n));
cin >> n;
}
}
You get SIGSEGV on dp(0) call. It causes an infinite recursion.
By the way, your solution is wrong, for example the answer for 24 is not 25. Try to avoid magic constants, it is just enough to set a[0] = 0 and make a more accurate dp function:
uint32_t dp(uint32_t n) {
if (a.find(n) == a.end())
a[n] = max(n, dp(n / 2) + dp(n / 3) + dp(n / 4));
return a[n];
}
As can be seen above, 32-bit type is enough to store any possible answer.