您的位置:首页 > 其它

HDU2842之斐波那契亚数列变形,动态规划

2014-07-18 15:25 239 查看
1.原题展示:

一根棒子上有n个环(n<=10^9) 第一个环可以随意取下或者放上 如果前k个环都不在棒子上,且第k+1个环在棒子上,则你可以取下或放上第k+2个环 给出n,求最少需要多少步可以取完棒子上的环?

2.思路分析:

如果要把n个环全部拿完,那么我们必须先拿完前n-2个环(只有这样才能拿走第n个环),剩下第n-1个环未拿走。当拿走前n-2个环所花的步骤数目为f(n-2)加上最后一个环,那么所花步数为f(n-2)+1步.对于第n-1个球,如果要拿走它,必须补上前n-2个球,放进去n-2个球所花步数为f(n-2)步。此时棒上共有n-1个环,要全部拿走,则所需步数为f(n-1)步。到这里我们就可以得到递推公式了:f(n)=f(n-2)+1+f(n-2)+f(n-1)=2*f(n-2)+f(n-1)+1.

对于这种递推公式,我们考虑矩阵的快速幂求法,首先我们需要构造出几个基本矩阵。

矩阵init:

1 2 1

1 0 0

0 0 1

矩阵r:(构造有技巧,每次乘init后第一列都会按照f(k-1),f(k-2),1的格式排列,因而保证了递推关系的成立)

2 0 0

1 0 0

1 0 0

所以f(n)为矩阵init^n-2*r的第一个数字。

3.代码如下:

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#define size 3
#define mod 200907
struct Mat
{
long long num[size][size];
};
Mat init,r;//定义全局变量;
void InitMat()//初始化全局变量函数
{
int i,j;
for (i=0;i<size;i++)
for (j=0;j<size;j++)
r.num[i][j] = init.num[i][j] = 0;
r.num[1][0] = r.num[2][0] = init.num[0][0]=init.num[0][2]=init.num[1][0]=init.num[2][2]=1;
r.num[0][0] = init.num[0][1]= 2;
}

Mat mul(Mat m,Mat r)//矩阵相乘
{
Mat c;
memset(c.num,0,sizeof(c.num));
for (int i=0;i<size;i++)
{
for (int j=0;j<size;j++)
{
c.num[i][j] = 0;
for(int k=0;k<size;k++)
c.num[i][j]+=(m.num[i][k]*r.num[k][j])%mod;
c.num[i][j]%=mod;
}//矩阵相乘并赋给ans

}
return c;//返回最后的值
}
Mat pow(Mat m,int k)//矩阵的乘方函数
{
Mat ans;
memset(ans.num,0,sizeof(ans.num));//首先置为0
for(int i=0;i<size;i++)
for(int j=0;j<size;j++)
if(i==j) ans.num[i][j]=1;//置为单位矩阵
while(k)
{
if(k&1) ans = mul(ans,m);//开始矩阵的乘方
k >>= 1;
m = mul(m,m);
}
return ans;//返回所求矩阵
}
int main()
{
int n;
InitMat();
while (scanf("%d",&n)!=EOF)
{
if(n==1) printf("1\n");
else if(n==2) printf("2\n");
else
{
Mat multi=pow(init,n-2);
Mat t=mul(multi,r);
printf("%lld\n",t.num[0][0]);
}
}
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: