DP + math 之 Codeforces 126D - Fibonacci Sums

来源:互联网 发布:js怎么创建二维数组 编辑:程序博客网 时间:2024/06/10 18:40
//  [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 + 313 = 8 + 516 = 13 + 2 + 113 = 8 + 3 + 216 = 8 + 5 + 316 = 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); */
#include <iostream>#include <cstdlib>#include <cstdio>#include <algorithm>using namespace std;typedef __int64 int64;const int MAX = 90;int64 F[MAX];int get_index[MAX];int64 dp[MAX][2];void Fib(){F[0] = 1;F[1] = 1;for (int i = 2; i < 87; ++i) {F[i] = F[i - 1] + F[i - 2];}}int64 Solve(int len){dp[0][1] = 1;dp[0][0] = (get_index[0] - 1) >> 1;for (int i = 1; i < len; ++i) {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);}return (dp[len - 1][0] + dp[len - 1][1]);}int main(){//freopen("input.txt", "r", stdin);Fib();int T;scanf("%d", &T);while (T--) {int64 n;int len = 0;scanf("%I64d", &n);for (int i = 86; i >= 1; --i) {if (F[i] <= n) {n -= F[i];get_index[len++] = i;}}if (n) { // 若无法将 N 拆分成不等的Fibonacci数之和,输出 0。//(这里不判断也可以AC,但是目前我无法给出“任何一个正整数都可以用不等的Fibonacci数列表示”的数学证明)printf("0\n");continue;}reverse(get_index, get_index + len);printf("%I64d\n", Solve(len));}return 0;}
0 0
原创粉丝点击