URL: http://www.topcoder.com/stat?c=problem_statement&pm=4629
The problem asks us to compute the minimum number of Fibonacci numbers required to sum up to a given number n. If you are familiar with Zeckendorf's Theorem then you might be able to see a greedy strategy. Zeckendorf's Theorem states that every positive integer can be represented in a unique way as the sum of one or more distinct Fibonacci numbers such that it does not contain two consecutive Fibonacci numbers. A more obvious approach is to use dynamic programming, let D(n) denote the minimum number of Fibonacci terms to make up a sum of exactly n. Then we can define a recurrence relation such as: F(n) = min { 1 + F(n - T[k]) } where T[k] denotes the k-th Fibonacci term. Obviously F(n - T[k]) is only defined for n - T[k] >= 0. Additionally, the base case is defined as F(0) = 0, which acts as our stopping point as this means we have no remaining sum to make up for. Coding this is relatively simple:
class FibonacciSum {
public:
int howMany(int);
};
int tab[101];
int memo[1000001];
int cnt;
#define INF (1 << 20)
int func(int n) {
int res = INF;
if (n == 0) return 0;
if (memo[n] != -1) return memo[n];
for (int i = cnt; i >= 0; i--) {
if (n - tab[i] >= 0) res <?= 1 + func(n - tab[i]);
}
return memo[n] = res;
}
int FibonacciSum::howMany(int n) {
tab[0] = tab[1] = 1;
cnt = 2;
memset(memo,-1,sizeof(memo));
for (int i = 2; tab[i-1] + tab[i-2] < 1000000; i++, cnt++) {
tab[i] = tab[i-1] + tab[i-2];
}
cnt--;
return func(n);
}
We can also translate this into a bottom-up dynamic programming algorithm. To do this, we must consider the order of calculation - this is dictated by the recurrence relation we defined earlier. As this is a 1 dimensional DP problem, it's relatively simple: given an n value, we need to compute all values below n before considering n. This can be seen by the term inside the recursive call (n - T[k]), as T[k] is strictly positive for all k values we are guaranteed to only require all values below n to be calculated beforehand.
class FibonacciSum {
public:
int howMany(int);
};
int dp[1000001];
int tab[101];
#define INF (1 << 20)
int FibonacciSum::howMany(int n) {
tab[0] = tab[1] = 1;
int cnt = 2;
for (int i = 2; tab[i-1] + tab[i-2] < 1000000; i++, cnt++) {
tab[i] = tab[i-1] + tab[i-2];
}
dp[0] = 0;
for (int i = 1; i <= n; i++) {
dp[i] = INF;
for (int k = 1; k < cnt; k++) {
if (i - tab[k] < 0) break;
dp[i] = min(dp[i], 1 + dp[i - tab[k]]);
}
}
return dp[n];
}
No comments:
Post a Comment