声明
本文资料参考acwing算法基础课
地址:https://www.acwing.com
概述
- 解决问题:求大整数的各种运算
- 时间复杂度为O(size)
大整数表示
使用string读入,使用一个vector数组存储,从0位开始分别表示大整数由低到高的位上数字。
// 输入
string a;
vector<int> A;
cin >> a;
for (int i = a.size() - 1; i >= 0; i -- ) A.push_back(a[i] - '0');
// 输出
for (int i = A.size() - 1; i >= 0; i -- ) cout << A[i];
加法模板记忆
这个模板分为6个部分:
- 保长 :确保A比B长
- 初:初始化结果C和进位/求和位t
- 求和:每一轮加上A、B对应位
- 压位:把t % 10压入C
- 进位:把t / 10保留进位
- 判进位:结果的size可能比A大1,这样的话需要将进位位压入
加法
// AB求和返回一个vector
vector<int> add(vector<int> &A, vector<int> &B)
{
// 1保长
if (A.size() < B.size()) return add(B, A); // 确保A比B长
// 2初
vector<int> C; // 初始化结果数组
int t = 0; // 初始化累加或进位位
for (int i = 0; i < A.size(); i ++ )
{
// 3求和
t += A[i]; // 首先加上A和B
if (i < B.size()) t += B[i]; // 注意B的长度
// 4压位
C.push_back(t % 10); // 如果t是XY,把Y压入C
// 5进位
t /= 10; // 把X保留
}
// 6判进位
if (t) C.push_back(t); // C的size可能比A大1,所以特判一下
return C;
}
减法模板记忆
这个模板分为6个部分:
- 保号 :确保A>=B
- 初:初始化结果C和进位/求和位t
- 减法:t = A - t - B
- 压位:把(t+10) % 10压入C
- 借位:判t是否<0判断是否借位,用t保存
- 前导0:去除C的前导0
减法
// 比较A、B哪个大,返回A >= B
bool cmp(vector<int> &A, vector<int> &B)
{
if (A.size() != B.size()) return A.size() > B.size(); // 不等长化为比长度
for (int i = A.size() - 1; i >= 0; i -- )
if (A[i] != B[i]) return A[i] > B[i]; // 等长的时候一次从高位比较,第一个不同位大小
return true; // 完全相同返回true
}
// 1A减B返回一个vector(前提是A>B)
vector<int> sub(vector<int> &A, vector<int> &B)
{
// 2初
vector<int> C; // 初始化结果
int t = 0;
for (int i = 0; i < A.size(); i ++ )
{
// 3减法
t = A[i] - t; // A减去借位
if (i < B.size()) t -= B[i]; // B存在的话,减去相应的B
// 4压位
C.push_back((t + 10) % 10); // 这时t保存了A - B - 借位的数,可能是负数,直接压入+10再%10(如果不够减借了位)
// 5借位
if (t < 0) t = 1; // 借位的话要把t置1
else t = 0; // 没借位置0
}
// 最后一次一定不用借位,因为A>=B
// 把C.back()也就是数字的高位的0pop掉,注意只有一个的时候不要pop
// 6前导0
while (C.size() > 1 && C.back() == 0) C.pop_back();
return C;
}
乘法模板记忆
这个模板分为5个部分:
- 初:初始化结果C和进位/求和位t
- 累加:t = t + A * b
- 压位:把t的最低位压入C
- 更新累加:用压入后剩余位覆盖t
- 前导0:去除C的前导0,实际上只有b为0才用去除前导零,但是保险起见可以去除
乘法
// A乘b返回一个vector
vector<int> mul(vector<int> &A, int b)
{
// 1初
vector<int> C;
int t = 0;
for (int i = 0; i < A.size() || t; i ++ ) // 在A没有遍历完或t没有清空时继续循环
{
// 2累加
if (i < A.size()) t += A[i] * b; // t累加A*b
// 3压位
C.push_back(t % 10); // 假设t为XYZ,把Z压入
// 4更新累加
t /= 10; // 用XY代替原来的t进行下一轮累加
}
// 5前导0
while (C.size() > 1 && C.back() == 0) C.pop_back();
return C;
}
除法模板记忆
这个模板分为5个部分:
- 初:初始化结果C和进位/求和位t
- 临时被除数:t = t * 10 + A
- 压位:把新被除数除的结果压入C
- 余数:把新的余数放在t
- 倒置:压位是从最高位开始的,所以要倒置一下
- 前导0:去除C的前导0
除法
// A除以b余t
vector<int> div(vector<int> &A, int b, int &t)
{
// 1初
vector<int> C;
t = 0;
for (int i = A.size() - 1; i >= 0; i -- )
{
// 2临时被除数
t = t * 10 + A[i]; // 上一位的余数乘10+A的到新被除数(模仿除法竖式)
// 3压位
C.push_back(t / b); // 压入本次被除数的商
// 4余数
t %= b; // 得到余数
}
// 5倒置
reverse(C.begin(), C.end()); // 压入C是从高到低压的,要倒置
// 6前导0
while (C.size() > 1 && C.back() == 0) C.pop_back();
return C;
}