DP + math 之 Codeforces 126D - Fibonacci Sums

//  [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\n");
55             continue;
56         }
57         reverse(get_index, get_index + len);
58         printf("%I64d\n", Solve(len));
59     }
60     return 0;
61 }
 
posted @ 2014-07-12 21:26  JmingS  阅读(275)  评论(0编辑  收藏  举报