// [7/12/2014 Sjm] /* 题目: By the given number n determine the number of its possible different decompositions into Fibonacci sum. 前提: 1) F[86] 是 >=10^18 的最大Fibonacci数; 2) (由Fibonacci数列的构造式决定,举几个例子就可以知道了) 假设的Fibonacci数的下标区间为[m, n),包括 m, 对n进行拆分,其分解的数目是: (n-m)>>1 假设的Fibonacci数的下标区间为(m, n),不包括 m, 对n进行拆分,其分解的数目是: (n-m-1)>>1 思路: 用 N 减去(与N相等或小于N)的最大的Fibonacci数,并将得到的值付给N,不断循环,直至 N 为零。 (保证拆分出来的Fibonacci数的数目是最少的,使 dp 时不会漏掉情况) 将取得的所有Fibonacci数从小到大排列出来, 存储于 get_index[] (存储的是所取得的Fibonacci的标号) 状态: dp[i][1]: 在拆分 N 时,已选取 get_index[i] 位置所代表的Fibonacci数, 此情况下不同分解的数目 (言外之意是:不可以将 get_index[i] 位置所代表的Fibonacci数拆了) dp[i][0]: 在拆分 N 时,没有选取 get_index[i] 位置所代表的Fibonacci数,此情况下不同分解的数目 (言外之意是:可以将 get_index[i] 位置所代表的Fibonacci数用其他Fibonacci数拆了) 分析: 1) dp[i][1]的情况: 由于不可以将 get_index[i] 位置所代表的Fibonacci拆数了, 故决定dp[i][1]的因素是 get_index[i-1] 位置所代表的Fibonacci数 2) dp[i][0]的情况: 此时 get_index[i] 位置所代表的Fibonacci数可拆, 故 在 dp[i-1][0] 的基础上: 需要分解的Fibonacci数下标区间为 [get_index[i-1], get_index[i]) 在 dp[i-1][1] 的基础上: 需要分解的Fibonacci数下表区间为 (get_index[i-1], get_index[i]) (求解方法,参见前提) 至于为什么这样分解,可以看一下这样几个例子(关键看从上到下的排列方式): 例1: 13 = 13 例2: 16 = 13 + 3 13 = 8 + 5 16 = 13 + 2 + 1 13 = 8 + 3 + 2 16 = 8 + 5 + 3 16 = 8 + 5 + 2 + 1 决策: 初始状态: dp[0][1] = 1, dp[0][1] = (get_index[i] - 1)>>1; dp[i][1] = dp[i - 1][1] + dp[i - 1][0]; dp[i][0] = dp[i - 1][0] * ((get_index[i] - get_index[i - 1]) >> 1) + dp[i - 1][1] * ((get_index[i] - get_index[i - 1] - 1) >> 1); */
1 #include <iostream> 2 #include <cstdlib> 3 #include <cstdio> 4 #include <algorithm> 5 using namespace std; 6 7 typedef __int64 int64; 8 const int MAX = 90; 9 10 int64 F[MAX]; 11 int get_index[MAX]; 12 int64 dp[MAX][2]; 13 14 void Fib() 15 { 16 F[0] = 1; F[1] = 1; 17 for (int i = 2; i < 87; ++i) { 18 F[i] = F[i - 1] + F[i - 2]; 19 } 20 } 21 22 int64 Solve(int len) 23 { 24 dp[0][1] = 1; 25 dp[0][0] = (get_index[0] - 1) >> 1; 26 for (int i = 1; i < len; ++i) { 27 dp[i][1] = dp[i - 1][1] + dp[i - 1][0]; 28 dp[i][0] = 29 dp[i - 1][0] * ((get_index[i] - get_index[i - 1]) >> 1) 30 + dp[i - 1][1] * ((get_index[i] - get_index[i - 1] - 1) >> 1); 31 } 32 return (dp[len - 1][0] + dp[len - 1][1]); 33 } 34 35 int main() 36 { 37 //freopen("input.txt", "r", stdin); 38 Fib(); 39 int T; 40 scanf("%d", &T); 41 while (T--) { 42 int64 n; 43 int len = 0; 44 scanf("%I64d", &n); 45 for (int i = 86; i >= 1; --i) { 46 if (F[i] <= n) { 47 n -= F[i]; 48 get_index[len++] = i; 49 } 50 } 51 if (n) { 52 // 若无法将 N 拆分成不等的Fibonacci数之和,输出 0。 53 //(这里不判断也可以AC,但是目前我无法给出“任何一个正整数都可以用不等的Fibonacci数列表示”的数学证明) 54 printf("0 "); 55 continue; 56 } 57 reverse(get_index, get_index + len); 58 printf("%I64d ", Solve(len)); 59 } 60 return 0; 61 }