目录
高精度加法——P1601 A+B Problem(高精) - 洛谷
高进度减法——P2142 高精度减法 - 洛谷
高精度乘法——P1303 A*B Problem - 洛谷
高精度除法——P1480 A/B Problem - 洛谷
之前我们遇到数字的时候,遇到的数的时候我们可以使用int来存储,如果说这个数字达到了
,那我们可以使用long long来存储。但是要是数值再比这个都大的时候,比如说当数据的数值达到了
,这样子我们用什么去存储啊???
当数据的值特别⼤,各种类型都存不下的时候,此时就要⽤⾼精度算法来计算加减乘除:
- 先⽤字符串读⼊这个数,然后⽤数组逆序存储该数 的每⼀位;
- 利⽤数组,模拟加减乘除运算的过程。
⾼精度算法本质上还是模拟算法,⽤代码模拟⼩学列竖式计算加减乘除的过程。
高精度加法——P1601 A+B Problem(高精) - 洛谷
其实就是模拟小学列竖式来计算。
- 先⽤字符串读⼊这个数,然后⽤数组逆序存储该数的每⼀位;
- 利⽤数组,模拟加减乘除运算的过程。
第一步是:先用字符串读入,拆分每一位,逆序放到数组中
就像下面这样子
那我们怎么根据原字符串里面的数字来放到数组里面呢?
大家有没有发现,逆序存储的字符的下标和原来对应字符的下标的和就是字符串的长度-1啊!!!
这很容易理解了吧!!
然后还有一个要注意的是,我们是将字符转换成int存放到数组里面。
所以我们得将每个数字字符减去'0'得到到的才是真正的数字。
接下来执行第2步:利⽤数组,模拟加减乘除运算的过程。
这个过程其实很容易理解,我们先看看下面这个过程
其实就下面这个部分
- 对应位置相加,然后加上进位
- 处理进位:x/10
- 处理余数
我们就重复这3步即可。很简单的啦
#include<bits/stdc++.h> // 包含几乎所有标准库的头文件,用于快速编程
using namespace std; // 使用标准命名空间,避免每次调用标准库时都需要加std::前缀// 定义大数处理的最大长度
const int N = 1e6 + 10;
int ra[N]; // 存储第一个大数的每一位(逆序)
int rb[N]; // 存储第二个大数的每一位(逆序)
int sum[N]; // 存储两个大数相加的结果(逆序)int lc; // 最长数的长度,用于在加法过程中跟踪结果数组的大小// 大数加法函数
void add(int* a, int* b, int* c)
{// 遍历每一位进行加法运算for(int i = 0; i < lc; i++){// 注意:这里使用+=而不是=,但在这个特定例子中,由于sum数组在调用前未初始化,c[i] += a[i] + b[i]; // 计算当前位的和(包括之前的进位)c[i + 1] += c[i] / 10; // 计算进位并加到下一位c[i] %= 10; // 取当前位的个位数}// 如果最高位有进位,则增加lc的值if(c[lc]) lc++;
}int main()
{string a, b; // 存储输入的两个大数cin >> a >> b; // 读取输入// 设置lc为两个数中较长的一个的长度lc = max(a.size(), b.size());// 将数据逆序存储到数组中(因为字符串是从高位到低位存储的,而我们需要从低位开始加)for(int i = 0; i < a.size(); i++){ra[i] = a[a.size() - 1 - i] - '0'; // 将字符转换为数字并逆序存储}for(int i = 0; i < b.size(); i++){rb[i] = b[b.size() - 1 - i] - '0'; // 将字符转换为数字并逆序存储}// 调用大数加法函数add(ra, rb, sum);// 从最高位开始逆序打印结果(因为我们是从低位开始加的,所以结果需要逆序输出)for(int i = lc - 1; i >= 0; i--){cout << sum[i]; // 输出每一位数字}return 0; // 程序结束
}
高进度减法——P2142 高精度减法 - 洛谷
说实话,这题和上一题好像是差不多的,我们的思路还是和下面这个一样
首先我们肯定是要使得a-b的,但是我们要考虑负数的情况。我们仔细想想上面那种列竖式的解法还能适用于小数减去大数吗?
其实是不能的,所以我们要保证我们是在大数减去小数,对于负数的情况,我们就在结果前面添加一个负号即可。这就很简单啦!!
对此我们就需要先比较a和b表示的值谁大!!
然后再按照下面这个步骤即可。
这题其实还是很简单的
#include<bits/stdc++.h> // 包含几乎所有标准库,通常用于竞赛编程,不推荐在生产环境中使用
using namespace std;const int N = 1e6 + 10; // 定义一个大常数N,用于数组的大小,足够存储非常大的整数int ra[N], rb[N], res[N]; // 定义三个数组,分别用于存储被减数、减数和结果
int lc; // 定义变量lc,用于存储结果数组的实际长度// 定义一个比较函数,用于比较两个字符串表示的数字大小
bool cmp(string a, string b) {if (a.size() == b.size()) {return a < b; // 当长度相同时,字典序较小的数字实际上更大(因为我们后面要做a-b)}return a.size() < b.size(); // 长度较短的数字更小
}// 实现大整数减法
void sub(int* a, int* b, int* c) {for (int i = 0; i < lc; i++) {c[i] += a[i] - b[i]; // 执行减法操作if (c[i] < 0) { // 如果当前位结果为负c[i + 1] -= 1; // 向高位借位c[i] += 10; // 当前位加上10(相当于加上从高位借来的10)}}// 处理结果中的前导零while (lc > 1 && c[lc - 1] == 0) lc--;
}int main() {string a, b;cin >> a >> b; // 输入两个大整数if (cmp(a, b)) { // 如果b比a大,则交换a和b,并标记结果为负swap(a, b);cout << '-';}lc = max(a.size(), b.size()); // 设置结果数组的长度为两个输入中较大的长度// 将字符串表示的整数逆序存储到数组中,方便从低位开始处理for (int i = 0; i < a.size(); i++) {ra[i] = a[a.size() - i - 1] - '0';}for (int i = 0; i < b.size(); i++) {rb[i] = b[b.size() - i - 1] - '0';}sub(ra, rb, res); // 执行减法// 从高位到低位输出结果数组中的数字for (int i = lc - 1; i >= 0; i--) {cout << res[i]; }return 0; // 程序结束
}
高精度乘法——P1303 A*B Problem - 洛谷
这题我们采用新战术,我们使用无进位相加。
⽆进位相乘再相加:
- • 还是「列竖式」,但是每⼀位相乘的时候不考虑进位,直接把乘的结果放在对应位上;
- • 等到所有对应位置「乘完」并且「累加完」之后,「统⼀处理进位」。
还是很简单的
#include<bits/stdc++.h>
using namespace std;const int N=1e6+10;
int ra[N],rb[N],c[N];
int la,lb,lc;void mul(int*a,int*b,int*c)
{// ⽆进位相乘,然后相加for(int i=0;i<la;i++){for(int j=0;j<lb;j++){c[i+j]+=a[i]*b[j];}}for(int i=0;i<lc;i++){c[i+1]+=c[i]/10;c[i]%=10;}while(lc>1&&c[lc-1]==0) lc--;
}
int main()
{string x, y;cin >> x >> y; // 输入两个大整数// 1. 拆分每一位,逆序放在数组中la = x.size(); // 被乘数长度lb = y.size(); // 乘数长度lc = la + lb; // 结果数组长度(初始估计为两个数长度之和,后续可能调整)for (int i = 0; i < la; i++) {ra[la - 1 - i] = x[i] - '0'; // 被乘数逆序存储}for (int i = 0; i < lb; i++) {rb[lb - 1 - i] = y[i] - '0'; // 乘数逆序存储}// 2. 模拟乘法的过程mul(ra, rb, c); // c = a * b(即ra数组和rb数组的乘积存储在c数组中)// 3. 输出结果for (int i = lc - 1; i >= 0; i--) {cout << c[i]; // 从高位到低位输出结果数组中的数字}return 0; // 程序结束
}
高精度除法——P1480 A/B Problem - 洛谷
说实话这题还是很简单的!
模拟⼩学「列竖式」计算「两数相除」的过程(注意,我们这⾥是「⾼精度÷低精度」)。
我们注意到b最大是,这就意味着我们不用数组来表示了,我们直接使用int来表示b。这个和前面几题有点不一样,所以我们需要借助一个指针来遍历这个被除数的每一位。
定义⼀个指针 从「⾼位」遍历被除数,⼀个变量 标记当前「被除的数」,记除数是b ;
- 更新⼀个当前被除的数 t =t×10+a[i] ;
- t/b表⽰这⼀位的商, t%b表⽰这⼀位的余数;
- ⽤t记录这⼀次的余数,遍历到下⼀位的时候重复上⾯的过程
被除数遍历完毕之后, ⾥⾯t存的就是余数,但是商可能存在前导0 ,注意清空。
现在就很简单了.
#include<bits/stdc++.h> // 包含几乎所有标准库的头文件,方便但不推荐在生产环境中使用
using namespace std;const int N=1e6+10; // 定义常量N,用于表示数组的最大长度,这里考虑到大整数的情况
typedef long long LL; // 定义长整型别名LLint ra[N], b, res[N], la, lc; // 定义数组ra存储反转后的大整数,b为除数,res存储结果,la和lc分别为输入和结果的长度// 函数:对反转后的大整数数组ra进行除法运算,结果存储在res数组中
void sub(int*a,int*c)
{LL t = 0; // 标记每次除完之后的余数,初始化为0for(int i=la-1;i>=0;i--) // 从大整数的最低位开始遍历{t=t*10+a[i]; // 将当前余数乘以10(向左移动一位),并加上当前位的数字c[i]=t/b; // 计算当前位的商t%=b; // 更新余数}// 处理前导0(如果结果的最高位是0,则结果长度需要减1,直到不是0或长度为1)while(lc > 1 && c[lc - 1] == 0) lc--;
}int main()
{string a; // 定义字符串变量a用于存储输入的大整数cin>>a>>b; // 输入大整数和除数la=a.size(); // 获取输入大整数的长度for(int i=0;i<a.size();i++){ra[i]=a[a.size()-i-1]-'0'; // 将大整数反转并存储到数组ra中,同时转换为数字}lc=la; // 初始化结果数组的长度为输入数组的长度(最终会根据实际情况调整)sub(ra,res); // 调用sub函数进行除法运算for(int i = lc - 1; i >= 0; i--) cout << res[i]; // 从结果的最高位开始输出,注意要反转回来
}