洛谷p1225 c++(使用高精度)

题解:


一开始我这个代码想到的是使用递归来求解

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;
}

  • 16
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值