高精度① 洛谷P1480

高精度算法①

实际上高精度就是说参与运算的数据和运算结果的范围,超出标准数据类型能表示的数据大小范围的运算。对于此类超大数据我们通常采用数组来存储这个数字的每一位数,然后模拟加减乘除的运算法则进行运算。根据难度排序有:
1.高精度除高精度(暂时还不会)
2.高精度减高精度(暂时还不会)
3.高精度乘高精度(这个暂时会,P1303 A*B Problem)
4.高精度加高精度(P1601 A+B Problem)
5.高精度除低精度(P1480 A/B Problem)

目录

1.1我的题解
1.2佬①的题解
1.3佬②的题解
1.4 佬③我修改后的题解
1.5 易犯的错误、需要注意的点
1.6总结

OK进入正题

在这里插入图片描述
注意到a,b的取值范围其中a明显是超出数据范围的数据,b恰好是int类型范围内的数据,籍此我们可以判断这是一个高精度除低精度的题目
(顺路复习下数值范围)

:类型名称字节数取值范围备注
signed char1-2^7(-128) ~ 2^7-1(127)10^2
short2-2^15(-32 768) ~ 2^15-1(32 767)10^4
int4-2^31(-2 147 483 648) ~ 2^31-1(2 147 483 647)10^9
long4-2^31(-2 147 483 648) ~ 2^31-1(2 147 483 647)10^9
long long8-2^63(-9.2233720368548e+18) ~ 2^63-1(9.2233720368548e+18)10^18

我の题解 VS 佬的题解

我の题解:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
int main(){
	//定义部分
    int len1,b,m=0,d=0,flag=1;
    char a[20000];
    int c[20000],e[20000];
    //数据预处理:将超出范围的大整数以字符串形式存入a内,再转化为int类型的数组
    scanf("%s",a);getchar();
    scanf("%d",&b);
    len1=strlen(a);//测出数字的长度方便后续循环处理
	
    memset(c,0,sizeof(c));
    memset(e,0,sizeof(c));//将数组c,e全部置为0

    for(int i=1;i<=len1;i++){
        c[i]=a[i-1]-'0';//下标从1开始,将字符形式的数字转化为int类型,
        //故要减去字符形式的‘0’,此时asc2码的差值即该数字
    }
	//算法核心:模拟除式
    for(int i=1;i<=len1;i++){
        e[i]=((m*10+c[i])/b);
        //(上一位被除数的余数乘10加现在这一位被除数)/除数
        m=(m*10+c[i])%b; 
        //下一位的余数=(上一位被除数的余数乘10加现在这一位被除数)%除数
    }
    //

	//输出部分:
    for(int i=1;i<=len1;i++){
        while(e[i]==0&&flag==1&&i<len1){
            if(e[i+1]!=0){
                flag=0;
                i++;
                break;
            }//去除无用的前缀0
            i++;
        }
        printf("%d",e[i]);
    }
    getchar();
}
佬の题解
#include<string>
#include<iostream>
using namespace std;
string a,c;
int b,i,d;
int main()
{
    cin>>a>>b;   //神奇的读入
    for (;i<a.length();i++)a[i]-=48;   //字符串转数字
    for (i=0;i<a.length();i++)
        c.push_back((d*10+a[i])/b+48),d=(d*10+a[i])%b;  //模拟竖式
        //佬的这个逗号用的很炫技,省了两个括号的位置
    for (i=0;c[0]==48;i++)c.erase(c.begin(),c.begin()+1);   //去0
    cout<<c;   //华丽的输出
    return 0;     //完美的结束
}

要看懂佬的操作还要有一定的知识储备:

.push_back():在字符串或vector后插入一个字符
.erase(size_type pos=0, size_type n=npos):删除字符:从pos处开始,删除 npos个字符

可以看出思路是一样的,
但是不得不说起码佬的去零方法比我简便多了,佬是利用string的函数将字符串首的0都去除了。
也可以看出我memset了一通没有任何用。可以直接把数组开在全局变量中,这样初值为0,局部变量的话会随机赋初值

#### 佬②の题解
#include<bits/stdc++.h>
using namespace std;
long long a,b,f,n,pd=0;
//a:被除数中的某一位数字 ,f:商中的某一位数字 ,n:除法算式中每次剩下来的余数 ,pd:判断是否输出过数字 
string p;
int main(){
	cin>>p>>b;
	for(int x=0;x<p.size();x++){a=p[x]-48,n=n*10+a,f=n/b,n%=b;if(pd||f)pd=1,cout<<f;}
	//计算出这一位的商 ,并且判断能否输出
	return 0;//望管理员通过
}

可以看出佬②并不比佬①朴实多少
把佬②的程序拆解一下

for(int x=0;x<p.size();x++){
a=p[x]-48,n=n*10+a,f=n/b,n%=b;
if(pd||f)pd=1,cout<<f;
}

思路还是一样,佬②是每得出一个数就输出
佬②的去前缀0的方法也很高级,pd或f有一个为真,pd即为假,所以截止到f第一次不为0,之前为f=0的时候均不输出。

我修改后的题解:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
int main(){
	//定义部分
    int b,m=0,d=0,flag=0;
    char a[20000];
    int c[20000];
    scanf("%s",a);getchar();
    scanf("%d",&b);

    for(int i=1;i<=strlen(a);i++){
   		 c[i]=a[i-1]-'0';
        e[i]=((m*10+c[i])/b);
        m=(m*10+c[i])%b; 
    } 

	//输出部分:
    for(int i=1;i<=len1;i++){
        if(flag||e[i])flag=1,printf("%d",e[i]);
    }
    getchar();
}

所以:我 == 佬!!!
在这里插入图片描述

易犯的错误、需要注意的点
  1. 数组要开的足够大,否则RE
  2. 注意去除前缀0
  3. %s读取字符串后记得getchar() 掉 ‘\n’

总结

本题不是很难,注意细节,记得高精度的核心:

用数组存储越界数字的每一位,通过模拟加减乘除来得到运算结果

高精度除低精度核心:

   for(int i=1;i<=len1;i++){
        e[i]=((m*10+c[i])/b);
        //(上一位被除数的余数乘10加现在这一位被除数)/除数
        m=(m*10+c[i])%b; 
        //下一位的余数=(上一位被除数的余数乘10加现在这一位被除数)%除数
    }
  
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值