2025年7月11日—基础算法—高精度
文章目录
- 0.算法介绍
- 1.A+B Problem(高精)
- 1.1 题目速览
- 1.2.算法设计
- 1.3.代码实现
- 2.高精度减法
- 2.1 题目速览
- 2.2 算法设计
- 2.3 代码实现
- 3.A*B Problem
- 3.1 题目速览
- 3.2 算法设计
- 3.3 代码实现
- 4. A/B Problem
- 4.1 题目速览
- 4.2 算法设计
- 4.3 代码实现
- 5.算法模版总结
0.算法介绍
当数据的值特别大,各种类型都存不下的时候,此时就要用高精度算法来计算加减乘除:
- 先用字符串
string
读入这个数,然后用数组逆序存储该数的每一位; - 利用数组,模拟加减乘除运算的过程。
高精度算法本质上还是模拟算法,用代码模拟小学列竖式计算加减乘除的过程。
1.A+B Problem(高精)
1.1 题目速览
题目链接:P1601 A+B Problem(高精) - 洛谷
1.2.算法设计
这类题我们无法通过直接相加,因为位数过大会造成越界的情况
所以我们只能通过字符串将相加的数存起来,后通过数组直接模拟加法的竖式计算即可如下图:
竖式计算的加法是从末尾向前加的,所以我们需要将字符串反向存到数组中
我们可以通过找对应下标的规律来逆向存放(这也是我们本章高精度的所有题目的基础,后续就默认已经逆向存放好了):
接下来是模拟列竖式计算的过程:
-
对应位累加:直接用第三个数组存入
这里第三个数组的长度我们在加法中可以先确定和最大的那个数的长度保持一致,后续在根据实际结果进行调整
-
处理进位:前一位等于当前为/10就是进位–>
arr[i+1] = arr[i]/10
-
处理余数:当前为
%10
取余即可 -
确定结果位数:后续还需要处理如下图这种,如果从最长的三位数变为四位数的时候,需要判定一下数组的最后一位是否为0,不为0直接将长度加上
1.3.代码实现
#include <iostream>
using namespace std;
#include <string>const int N = 1e6 + 10;
//变量如果定义为全局变量就无需我们等会在向函数传参了
int a[N], b[N], c[N];
int la, lb, lc;
string s1, s2;//高精度加法主要逻辑
void add()
{for (int i = 0; i < lc; i++){c[i] += a[i] + b[i]; //对应位相加c[i + 1] = c[i] / 10; //处理进位c[i] %= 10; //处理余数}if (c[lc]) lc++; //处理结果位数,如从三位数变为四位数
}
int main()
{cin >> s1 >> s2;//1.拆分每一位,逆序存放到数组中la = s1.size(), lb = s2.size();lc = max(la, lb);for (int i = 0; i < la; i++) a[i] = s1[la - 1 - i] - '0';for (int i = 0; i < lb; i++) b[i] = s2[lb - 1 - i] - '0';//2.模拟加法过程add();//输出结果for (int i = lc - 1; i >= 0; i--) cout << c[i];return 0;
}
2.高精度减法
2.1 题目速览
题目链接:P2142 高精度减法 - 洛谷
2.2 算法设计
竖式减法模拟过程如下:
由于是减法,所以无法直接相减,需要大数减小数,小数减大数可以转化为:先加一个负号,再算大数减小数
那么字符串的比较大小就需要注意字典序了:
也就是需要先判断长度是否一致,一致了才能直接比较,不一致则长度大的数值大,比较代码如下:
bool cmp(string& s1, string& s2)
{//先比较长度if (s1.size() != s2.size()) return s1.size() < s2.size();//在按照字典序比较return s1 < s2;
}
接着还是要将字符串导致放入到数组中,与前一题的步骤一致
模拟列竖式计算的过程:
-
对应位求差:直接相减
这里第三个数组的长度仍然可以先确定和最大的那个数的长度保持一致,后续在根据实际结果进行调整
-
处理借位:对相减为负数的数进行结尾
-
确定结果位数:如下图,如果结果前面有0,就需要再写一个循环将前面的0消掉
2.3 代码实现
#include <iostream>
using namespace std;
#include <string>const int N = 1e6 + 10;
int a[N], b[N], c[N];
int la, lb, lc;
string s1, s2;//比较大小
bool cmp(string& s1, string& s2)
{//先比较长度if (s1.size() != s2.size()) return s1.size() < s2.size();//在按照字典序比较return s1 < s2;
}
//实现高精度减法
void sub()
{for (int i = 0; i < lc; i++){c[i] += a[i] - b[i]; //对应位置相减if (c[i] < 0){c[i + 1]--; //借位c[i] += 10;}}while (lc > 1 && c[lc - 1] == 0) lc--; //处理前导0
}
int main()
{cin >> s1 >> s2;//比较大小如果需要交换那么结果也就是负数了if (cmp(s1, s2)){swap(s1, s2);cout << '-';}//1.拆分每一位,逆序存放到数组中la = s1.size(), lb = s2.size();lc = max(la, lb); //结果的位数先确定为最大的,后面在处理得到真正的位数for (int i = 0; i < la; i++) a[i] = s1[la - 1 - i] - '0';for (int i = 0; i < lb; i++) b[i] = s2[lb - 1 - i] - '0';//2.模拟减法过程sub();//3.输出结果for (int i = lc - 1; i >= 0; i--) cout << c[i];return 0;
}
3.A*B Problem
3.1 题目速览
题目链接:P1303 A*B Problem - 洛谷
3.2 算法设计
与前面的不同,想要让我们的代码简单一些,就不能和竖式乘法一样,每次都进行进位了
我们可以相统一进行相加,在统一进行进位操作
乘法是需要两个数字全部进行枚举,且有存放的位置有一定对应关系如下图:
那么不难发现其实就是枚举数组当前位置的下标直接相加即可,这部分代码可写作:
for (int i = 0; i < la; i++)
{for (int j = 0; j < lb; j++){c[i + j] += a[i] * b[j];}
}
接着还是要将字符串导致放入到数组中,与前一题的步骤一致
模拟无进位相乘再相加的过程:
-
对应位求乘积:与前面一样直接加到第三个数组中
这里第三个数组的长度我们在可以预估成相乘最大值(两位数乘两位数,最大为四位数),后续在根据实际结果进行调整
-
乘完之后处理进位:与加法处理方式相同
-
处理前导0:与减法处理方式相同
3.3 代码实现
#include <iostream>
using namespace std;
#include <string>const int N = 1e6 + 10;
int a[N], b[N], c[N];
int la, lb, lc;
string s1, s2;//模拟高精度乘法
void mul()
{//无进位相乘,在相加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;}//处理前导0while (lc > 1 && c[lc - 1] == 0) lc--;
}
int main()
{cin >> s1 >> s2;la = s1.size(), lb = s2.size();lc = la + lb;//1.拆分每一位,逆序存放到数组中for (int i = 0; i < la; i++) a[i] = s1[la - 1 - i] - '0';for (int i = 0; i < lb; i++) b[i] = s2[lb - 1 - i] - '0';//2.模拟乘法过程mul();//3.输出结果for (int i = lc - 1; i >= 0; i--) cout << c[i];return 0;
}
4. A/B Problem
4.1 题目速览
题目链接:P1480 A/B Problem - 洛谷
4.2 算法设计
这道题需要注意的是:不是真正的高精度/高精度,而是高精度/低精度
我们可以定义一个指针 i
从「高位」遍历被除数,一个变量 t
标记当前「被除的数」,记除数是 b
;
- 更新当前被除的数
t = t × 10 + a[i]
t/b
表示这一位的商,t%b
表示这一位的余数- 用
t
记录这一次的余数,遍历到下一位的时候重复上面的过程
被除数遍历完毕之后,t
里面存的就是余数,但是商可能存在前导 0,注意清空
4.3 代码实现
#include <iostream>
using namespace std;
#include <string>const int N = 1e6 + 10;
int a[N], c[N];
int la, b, lc;
string s1;//模拟高精度除法
void mul()
{long long t = 0; //标记每次除完之后的余数for (int i = lc - 1; i >= 0; i--){//计算当前的被除数t = t * 10 + a[i];c[i] = t / b;t = t % b;}//处理前导0while (lc > 1 && c[lc - 1] == 0) lc--;
}
int main()
{cin >> s1 >> b;la = s1.size();lc = la;//1.拆分每一位,逆序存放到数组中for (int i = 0; i < la; i++) a[i] = s1[la - 1 - i] - '0';//模拟除法过程mul();for (int i = lc - 1; i >= 0; i--) cout << c[i];return 0;
}
5.算法模版总结
那么我们将这几个题目的核心内容拿出就可以组成高精度算法的模版了,如下:
//高精度加法主要逻辑
void add()
{for (int i = 0; i < lc; i++){c[i] += a[i] + b[i]; //对应位相加c[i + 1] = c[i] / 10; //处理进位c[i] %= 10; //处理余数}if (c[lc]) lc++; //处理结果位数,如从三位数变为四位数
}//高精度减法主要逻辑
void sub()
{for (int i = 0; i < lc; i++){c[i] += a[i] - b[i]; //对应位置相减if (c[i] < 0){c[i + 1]--; //借位c[i] += 10;}}while (lc > 1 && c[lc - 1] == 0) lc--; //处理前导0
}//高精度乘法主要逻辑
void mul()
{//无进位相乘,在相加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;}//处理前导0while (lc > 1 && c[lc - 1] == 0) lc--;
}//高精度除法主要逻辑
void mul()
{long long t = 0; //标记每次除完之后的余数for (int i = lc - 1; i >= 0; i--){//计算当前的被除数t = t * 10 + a[i];c[i] = t / b;t = t % b;}//处理前导0while (lc > 1 && c[lc - 1] == 0) lc--;
}