HDU2842(不水)
题目描述:一根木棒上有n个环(n<=10^9) 第一个环可以随意取下或者放上 如果前k个环都不在棒子上,且第k+1个环在棒子上,则你可以取下或放上第k+2个环 给出n,求最少需要多少步可以取完棒子上的环?
思路:
- 设f[n]数组表示取下n个环所需最小次数;
- 若想让第n个环被取下,那么前(n-2)个都要被取下,第(n-1)要挂在环上;这时所需次数为f[n-2]+1;
- 考虑第(n-1)个环还未取下;而取下第(n-1)个环需要第(n-2)个环挂上,取下第(n-2)个环需要第(n-3)个环挂上…即先要把前(n-2个都取下来);还有f[n-1]它自己;以此类推,取下第(n-1)个环需要次数为f[n-2]+f[n-2];
- 合并得到递推公式为:f[n] = f[n-1] + 2 * f[n-2] + 1;
然后有个矩阵相乘,每次换值(有点像DP)
菲波那切数列的递推理解
类似于斐波那契,本题的递推公式为
A:
| f[n] |
| f[n-1] |
| 1 |
B:
| 1 2 1 |
| 1 0 0 |
| 0 0 1 |
C:
| f[n-2]|
| f[n-1]|
| 1 |
A=B*C
就这样
贴上翻找到的 AC代码:
大神的代码
#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==0) return 0;
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;
}