题解:
一开始我这个代码想到的是使用递归来求解
int digui(int n){
int sum=0;
if(n==1)
sum=1;
if(n==2)
sum=2;
if(n==1||n==2)
return sum;
if(n>2){
return sum+=digui(n-1)+digui(n-2);
}
}
但是后面发现明显超时,我试图用记忆化搜索来抢救一下,所以就有了下面代码
int digui(int n){
if (memo[n] != -1) {
return memo[n];
}
if (n == 1) {
memo[n] = 1;
} else if (n == 2) {
memo[n] = 2;
} else {
memo[n] = digui(n-1) + digui(n-2);
}
return memo[n];
}
memset(memo, -1, sizeof(memo)); //main函数中使用这个进行了数组初始化
确实,使用记忆化搜索保存了之后确实节约了一些时间,因为保存了一些中间结果1,避免了重复计算中间值,但是依旧是超时,处理的n<100,然后就翻起了题解。
然后就发现了大家在用斐波那契数列,并且需要配合高精度,确实是这样的。
题解(来自于洛谷下面这位博主)
#include<bits/stdc++.h>
using namespace std;
int a[5000],b[5000],c[5000];
int main()
{
int n;
int x=1;
cin>>n;
if(n<3)
{
cout<<n;
return 0;
}
a[1]=1;b[1]=2;
for(int i=3;i<=n;i++)
{
for(int j=1;j<=x;j++)
c[j]=a[j]+b[j];
for(int j=1;j<=x;j++)
{
if(c[j]>9)
{
c[j+1]=c[j+1]+c[j]/10;
c[j]%=10;
if(j+1>x)
x++;
}
}
for(int j=1;j<=x;j++)
a[j]=b[j];
for(int j=1;j<=x;j++)
b[j]=c[j];
}
for(int i=x;i>0;i--)
cout<<b[i];
return 0;
}
这个博主的思路很好,我也写了一篇高精度的文章c++高精度-CSDN博客但是在这篇文章中仅仅介绍了两个数相加一次的情况,并且相加几位是知道的(相加len位数)。
这里的话是要相加多次,每次的话相加位数是不同的,需要使用x变量来保存相加的位数,这个博主的思路是:先将所有位数对应相加,将处理进位的逻辑放在了单独的循环中,使得代码结构更加简洁和易读。
解释一下
if(c[j]>9)
{
c[j+1]=c[j+1]+c[j]/10;
c[j]%=10;
if(j+1>x)
x++;
}
对这段代码分析:
1.处理进位的时候,如果在最高位,第x位(目前的最高位)上面有进位,if(j+1>x) x++;就会x++,让相当于数字的位数加一,下次循环计算的时候就要多计算一位。这个思路我举得非常好,完美控制了必要的循环次数(不用计算0+0这种的)。
2.这道题目中我们只需要得到最后第n阶台阶的方法数,而得到第n阶台阶的方法数,需要保存的只有到第n-1和第n-2阶的方法数。这里是使用了拷贝的方式,让c数组永远保存和。
对这段代码的改进1(主要缩短了代码长度,但是可读性可能差点)
#include<bits/stdc++.h>
using namespace std;
int a[5000], b[5000], c[5000];
int main()
{
int n;
int x = 1;
cin >> n;
if (n < 3)
{
cout << n;
return 0;
}
a[1] = 1;
b[1] = 2;
for (int i = 3; i <= n; i++)
{
memset(c, 0, sizeof(c)); // 将 c 数组初始化为 0
//上面的博主的那段代码中没有这段,因为上面写的是分开写的,
//他是c[j] = a[j] + b[j]相当于对c数组进行了清空上一次循环的数据。
//一开始我这行memset代码就忘记写了,找了半天错误。
for (int j = 1; j <= x; j++)
{
c[j] += a[j] + b[j];//这里注意是+=不能写为=,因为c[j]可能包含从别的位进过来的
c[j + 1] += c[j] / 10;
if (j == x && c[j] >= 10)
{
x++;
}
c[j] %= 10;
}
for (int j = 1; j <= x; j++)
a[j] = b[j];
for (int j = 1; j <= x; j++)
b[j] = c[j];
}
for (int i = x; i > 0; i--)
cout << b[i];
return 0;
}
对这段代码的改进2()去除了拷贝过程,让a,b,c数组轮流存储两数和,但是没想到运行时间还比上面的慢,我不理解,求大家解答。
#include<bits/stdc++.h>
using namespace std;
int a[5000], b[5000], c[5000];
int main()
{
int n;
int x = 1;
cin >> n;
if (n < 3)
{
cout << n;
return 0;
}
int flag=1;
a[1] = 1;
b[1] = 2;
for (int i = 3; i <= n; i++)
{
if(i%3==0){
memset(c, 0, sizeof(c)); // 将 c 数组初始化为 0
for (int j = 1; j <= x; j++)
{
c[j] += a[j] + b[j];
c[j + 1] += c[j] / 10;
if (j == x && c[j] >= 10)
{
x++;
}
c[j] %= 10;
}
flag=1;
}
if(i%3==1){
memset(a, 0, sizeof(a)); // 将 c 数组初始化为 0
for (int j = 1; j <= x; j++)
{
a[j] += b[j] + c[j];
a[j + 1] += a[j] / 10;
if (j == x && a[j] >= 10)
{
x++;
}
a[j] %= 10;
}
flag=2;
}
if(i%3==2){
memset(b, 0, sizeof(b)); // 将 c 数组初始化为 0
for (int j = 1; j <= x; j++)
{
b[j] += a[j] + c[j];
b[j + 1] += b[j] / 10;
if (j == x && b[j] >= 10)
{
x++;
}
b[j] %= 10;
}
flag=3;
}
}
for (int i = x; i > 0; i--){
if(flag==1)
cout << c[i];
if(flag==2)
cout << a[i];
if(flag==3)
cout << b[i];
}
return 0;
}