论高精度(加减乘)(附洛谷 P1601 、 P2142 题解)

题目描述

输入两个整数 a, b,输出它们的和(∣a∣,∣b∣≤109)。

注意

  1. Pascal 使用 integer 会爆掉哦!
  2. 有负数哦!
  3. C/C++ 的 main 函数必须是 int 类型,而且 C 最后要 return 0。这不仅对洛谷其他题目有效,而且也是 NOIP/CSP/NOI 比赛的要求!

好吧,同志们,我们就从这一题开始,向着大牛的路进发。

任何一个伟大的思想,都有一个微不足道的开始。

输入格式

两个以空格分开的整数。

输出格式

一个整数。

输入输出样例

输入 #1复制

20 30

输出 #1复制

50
大家入门时都应做过这样一道题吧。
但请注意:题目给的数据范围是小于10^9.

如是我们可以给出下面的代码:

#include <iostream>
#include <cstdio>
//万能头: #include<bits/stdc++.h>
using namespace std;

int main()
{
    int a,b;
    cin >> a >> b;
    cout << a+b << endl;
    return 0;
}

但如果是10^100呢?(不考虑int_128)

我们常用的int、long long甚至是unsigned long long都无法满足需求。这时候——

高精度闪亮登场!

那么:高精度到底有多“精”呢?

说实话,高精度没有上限!

洛谷 P1601 A+B Problem

题目描述

高精度加法,相当于a+b problem,不用考虑负数.

输入格式

分两行输入。a,b≤10^500

输出格式

输出只有一行,代表a+b的值

输入输出样例

输入 #1复制

1
1

输出 #1复制

2

输入 #2复制

1001
9099

输出 #2复制

10100

这里我们可以看到:数据范围是10^500!

那么怎么写呢?

我们可以用之前讲过的字符数组来解决问题。

char a[505], b[505];

那么为什么不能用普通数组呢?

普通数组需要以换行来结束输入。辣么长一个数,你全存在数组中的一个变量中——你要高精度干嘛?字符数组则是输入一个字符就结束输入……于是你就将辣么长一个数分别存在了许多变量中(只不过之后转整数有些麻烦)

#include <bits/stdc++.h>
using namespace std;
char a[505], b[505];
int main(){
	scanf("%s", &a);
	scanf("%s", &b);

小学加法怎么算?

列        竖        式        !

举例:                        9        3        9

                        +         6        4        8

                        ———————1—————                //进位

                         1        5        8        7

那么我们就可以按这样的思路一位一位地加……

但是计算机需要知道你的位数,不然一直加加加……000000……

怎么确定位数呢?

:strlen !!!

strlen所作的是一个计数器的工作,它从内存的某个位置(可以是字符串开头,中间某个位置,甚至是某个不确定的内存区域)开始扫描,直到碰到第一个字符串结束符'\0'为止,然后返回计数器值(长度不包含'\0')。                                                                                        ——百度百科

#include <bits/stdc++.h>
using namespace std;
int  l1, l2;
char a[505], b[505];
int main(){
	scanf("%s", &a);
	scanf("%s", &b);
	l1=strlen(a);
	l2=strlen(b);
	

接下来就是将字符数组转换为整型数组的过程。(字符无法进行运算!)(注意倒序):

#include <bits/stdc++.h>
using namespace std;
int numa[505], numb[505] l1, l2, l, cnt2;
char a[505], b[505];
int main(){
	scanf("%s", &a);
	scanf("%s", &b);
	l1=strlen(a);
	l2=strlen(b);
	for(int i=l1-1; i>=0; --i){
		numa[l1-i-1]=a[i]-'0';        //第一种写法
	}
	for(int i=l2-1; i>=0; --i){
		numb[cnt2]=b[i]-'0';        //第二种写法
		cnt2++;
	}
	l=max(l1, l2);        //计算最长位数

为什么要计算最长的位数?

不然就像505+1654,只考虑后三位,那前面的1怎么办?

细节:提前存储好l,不然会很费时间(max函数也费时间)。

接下来就到了列竖式的时间了:

#include <bits/stdc++.h>
using namespace std;
int numa[505], numb[505], ans[505], l1, l2, l, cnt2;
char a[505], b[505];
int main(){
	scanf("%s", &a);
	scanf("%s", &b);
	l1=strlen(a);
	l2=strlen(b);
	for(int i=l1-1; i>=0; --i){
		numa[l1-i-1]=a[i]-'0';
	}
	for(int i=l2-1; i>=0; --i){
		numb[cnt2]=b[i]-'0';
		cnt2++;
	}
	l=max(l1, l2);
	for(int i=0; i<l; ++i){
		ans[i]+=numa[i]+numb[i];        //相加
		ans[i+1]=ans[i]/10;        //进位处理
		ans[i]%=10;
	}

如果有进位怎么办?

505+666=1171
不处理的话,输出会变成:171!

然鹅加法只能进一位,所以只处理一次就够了:

#include <bits/stdc++.h>
using namespace std;
int numa[505], numb[505], ans[505], l1, l2, l, cnt2;
char a[505], b[505];
int main(){
	scanf("%s", &a);
	scanf("%s", &b);
	l1=strlen(a);
	l2=strlen(b);
	for(int i=l1-1; i>=0; --i){
		numa[l1-i-1]=a[i]-'0';
	}
	for(int i=l2-1; i>=0; --i){
		numb[cnt2]=b[i]-'0';
		cnt2++;
	}
	l=max(l1, l2);
	for(int i=0; i<l; ++i){
		ans[i]+=numa[i]+numb[i];
		ans[i+1]=ans[i]/10;
		ans[i]%=10;
	}
    if(ans[l]){    //如果前面有数,即不为0
		l++;    //位数加1
	}

倒序输出:

#include <bits/stdc++.h>
using namespace std;
int numa[505], numb[505], ans[505], l1, l2, l, cnt2;
char a[505], b[505];
int main(){
	scanf("%s", &a);
	scanf("%s", &b);
	l1=strlen(a);
	l2=strlen(b);
	for(int i=l1-1; i>=0; --i){
		numa[l1-i-1]=a[i]-'0';
	}
	for(int i=l2-1; i>=0; --i){
		numb[cnt2]=b[i]-'0';
		cnt2++;
	}
	l=max(l1, l2);
	for(int i=0; i<l; ++i){
		ans[i]+=numa[i]+numb[i];
		ans[i+1]=ans[i]/10;
		ans[i]%=10;
	}
    if(ans[l]){    //如果前面有数,即不为0
		l++;    //位数加1
	}
    for(int i=l-1; i>=0; --i){
		printf("%d", ans[i]);
	}
	return 0;
}

完整代码(注释版):

#include <bits/stdc++.h>
using namespace std;
int numa[505], numb[505] l1, l2, l, cnt2;
char a[505], b[505];
int main(){
	scanf("%s", &a);
	scanf("%s", &b);
	l1=strlen(a);
	l2=strlen(b);
	for(int i=l1-1; i>=0; --i){
		numa[l1-i-1]=a[i]-'0';        //第一种写法
	}
	for(int i=l2-1; i>=0; --i){
		numb[cnt2]=b[i]-'0';        //第二种写法
		cnt2++;
	}
	l=max(l1, l2);        //计算最长位数
    for(int i=0; i<l; ++i){
		ans[i]+=numa[i]+numb[i];        //相加
		ans[i+1]=ans[i]/10;        //进位处理
		ans[i]%=10;
	}
    if(ans[l]){    //如果前面有数,即不为0
		l++;    //位数加1
	}
    for(int i=l-1; i>=0; --i){    //倒序输出
		printf("%d", ans[i]);
	}
	return 0;
}

附高精减及高精乘代码:

1.高精减:

#include<bits/stdc++.h>
using namespace std;
char a[20000],b[20000];
int c[20000],d[20000],e[20000];
int l1,l2,p;
bool k=0,m=0;
int main(){
    cin>>a>>b;
    l1=strlen(a);
    l2=strlen(b);
    p=l2;
    for(int i=0;i<=l1;i++){
		c[l1-i]=a[i]-'0';
	}
    for(int i=0;i<=l2;i++){
		d[l2-i]=b[i]-'0';
	}
    if(l1>=l2){
        p=l1;
        for(int i=1;i<=p;i++){
        	swap(c[i],d[i]);
		}
	}
    else{
    	m=1;
	} 
    for(int i=1;i<=p;i++){
    	swap(c[i],d[i]);
	}
    for(int i=p;i>=0;i--){
        if (c[i]>d[i]){
        	break;
		}
        if(c[i]<d[i]){
            k=1;
            break;
        }
    }
    if(k){
		m=1;
		for(int i=0;i<p;i++){
			swap(c[i],d[i]);
		}
	}
    for(int i=0;i<=p;i++){
        e[i]+=c[i]-d[i];
        if(e[i]<0){
        	e[i]+=10;
			e[i+1]-=1;
		}
    }
    k=0;
    if(m){
    	cout<<"-";
	}
    for(int i=0;i<p;i++){
        if(e[p-i]!=0||i==p-1){
        	k=1;
		}
        if(k){
        	cout<<e[p-i]; 
		}   
    }
    return 0;
}

2.高精乘:

#include <bits/stdc++.h>
using namespace std;
int numa[260], numb[260], ans[260], l1, l2, l, cnt2;
char a[260], b[260];
int main(){
	scanf("%s", &a);
	scanf("%s", &b);
	l1=strlen(a);
	l2=strlen(b);
	for(int i=l1-1; i>=0; --i){
		numa[l1-i-1]=a[i]-'0';
	}
	for(int i=l2-1; i>=0; --i){
		numb[cnt2]=b[i]-'0';
		cnt2++;
	}
	l=max(l1, l2);
	for(int i=0; i<l1; ++i){
		for(int j=0;j<l2;j++){
			ans[i]+=(numa[i]*numb[j])*pow(10,j);
		}
		ans[i+1]=ans[i]/10;
		ans[i]%=10;
	}
	if(ans[l]){
		l++;
	}
	for(int i=l-1; i>=0; --i){
		printf("%d", ans[i]);
	}
	return 0;
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值