这道题表面上是说斐波那契数列,看似是一道找规律的数学题,而实际上却是一道DP题。
大体思路:题目输入的数字很大,乍一看,好像需要求出几千位斐波那契数,实际上,只要求出87个斐波那契数就足够了。这也是这道题的第一步,求出小于等于10^18的斐波那契数,并保存在fib数组中。fib数组从1开始存储,则每个斐波那契数的下标对应它们在斐波那契数列中的位置。然后,从最大斐波那契数开始,找出一个组成n的一组斐波那契数的序号,并保存在seq数组中(详见代码)。用sort从小到大对seq排序,使得数组中的斐波那契数的下标从小到大排列,与dp数组的第一维下标对应。然后开始标准的动态规划,其中dp[i][1]表示拆分下标为seq[i]的斐波那契数时的情况数,dp[i][0]表示不拆分下标为seq[i]的斐波那契数时的情况数。初始状态,为dp[0][0]=1,dp[0][1]=(seq[0]-1)/2;状态转移方程为dp[i][0]=dp[i-1][0]+do[i-1][1],dp[i][1]=dp[i-1][0]*((seq[i]-seq[i-1]-1)/2)+dp[i-1][1]*((seq[i]-seq[i-1])/2)。要明白这个初始状态和状态转移方程,那就要明白斐波那契数列中第i个斐波那契数有几种替代方案,答案是(i-1)/2,想明白这个,上面的式子就好理解了。
补充:这是我第一次在CF上提交代码,发现要把DEV中的“system(“PAUSE”);”去掉,否则会WA。此外大家还可以参考一下几篇大神的博文:2014工大校赛题目以及解,codeforces 126D Fibonacci Sums 递推 DP,Codeforces 126D Fibonacci Sums 求n由任意的Sum(fib)的方法数 dp。
代码(GUN C++):
#include <iostream>
#include <algorithm>
#include <cstdio>
#define MAX 90
using namespace std;
__int64 fib[MAX],dp[MAX][2]; //dp[i][0]表示fib[seq[i]]不被拆分,dp[i][1]表示fib[seq[i]]被拆分
int seq[MAX];
void init()
{
int i;
fib[1]=1;
fib[2]=2;
for(i=3;i<MAX;i++) fib[i]=fib[i-1]+fib[i-2];
//for(i=1;i<MAX;i++) cout<<fib[i]<<'\t';
}
int main()
{
int i,t,c;
__int64 n;
init();
scanf("%d",&t);
while(t--)
{
scanf("%I64d",&n);
c=0;
for(i=MAX-1;i>0&&n!=0;i--)
{
if(fib[i]<=n)
{
seq[c++]=i;
n-=fib[i];
}
}
sort(seq,seq+c);
//for(i=0;i<c;i++) cout<<seq[i]<<endl;
dp[0][0]=1;
dp[0][1]=(seq[0]-1)/2;
for(i=1;i<c;i++)
{
dp[i][0]=dp[i-1][0]+dp[i-1][1];
dp[i][1]=dp[i-1][0]*((seq[i]-seq[i-1]-1)/2)+dp[i-1][1]*((seq[i]-seq[i-1])/2);
}
printf("%I64d\n",dp[c-1][0]+dp[c-1][1]);
}
//system("PAUSE"); 提交CF