大整数相加
个人认为用 vector<int>
是最好的,也见过很多用字符串来写,但是字符串的单位是char,只有8位,乘法运算中,可能会出现溢出的问题。
模拟竖式运算。把数的每个数字放到 vector<int>
,然后把数组倒过来,方便做进位运算。因为最高位补1,对数组来说 push_front
代价太大。做完运算后逆序输出。
例题: https://www.luogu.org/problem/P1601
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <vector>
using namespace std;
//进位
void carry(vector<int>& a) {
for (int i = 0; i < a.size(); i++) {
//如果有进位,并且不是最高位,向高位进位
if (i != a.size() - 1 && a[i] >= 10) {
a[i + 1] += 1;
a[i] -= 10;
}
//如果有进位,且是最高位,高位添1
else if (a[i] >= 10) {
a.push_back(1);
a[i] -= 10;
}
}
}
// a = a + b
void add(vector<int>& a, vector<int>b) {
//避免下面循环越界,让a的位数长于b
if (b.size() > a.size())a.swap(b);
//逐位相加
for (int i = 0; i < b.size(); i++) {
a[i] += b[i];
}
carry(a);
}
int main() {
vector<int>a, b;
string s1, s2;
cin >> s1 >> s2;
//将输入的数字倒过来存,倒过来更方便运算
for (int i = s1.length() - 1; i >= 0; i--) {
a.push_back(s1[i] - '0');
}
for (int i = s2.length() - 1; i >= 0; i--) {
b.push_back(s2[i] - '0');
}
add(a, b);
//倒过来输出
for (int i = a.size() - 1; i >= 0; i--) {
printf("%d", a[i]);
}
return 0;
}
大整数相乘
普通高精
倒过来运算,和加法差不多,最后统一进一次位。int
范围足够了,不会溢出
例题: https://www.luogu.org/problem/P1303
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <vector>
using namespace std;
void carry(vector<int>& a) {
for (int i = 0; i < a.size(); i++) {
if (i != a.size() - 1 && a[i] >= 10) {
a[i + 1] += a[i] / 10;
a[i] %= 10;
}
else if (a[i] >= 10) {
a.push_back(a[i] / 10);
a[i] %= 10;
}
}
//if (a[a.size() - 1] == 0) a.pop_back();
//一般两个非零数相乘,最多只需要pop一次,但如果是0*某个数的话,这里就不只一次了
while (a.size() > 1 && a[a.size() - 1] == 0) a.pop_back();
}
//a=a*b
void multi(vector<int>& a, vector<int>b) {
//乘法运算,开两个数位数之和,确保不会超范围
vector<int>t(a.size() + b.size());
if (a.size() < b.size())a.swap(b);
int k = 0;
for (int i = 0; i < b.size(); i++) {
for (int j = 0; j < a.size(); j++) {
t[j + k] += (a[j] * b[i]);
}
k++;
}
carry(t);
a = t;
}
int main() {
vector<int>a, b;
string s1, s2;
cin >> s1 >> s2;
for (int i = s1.length() - 1; i >= 0; i--) {
a.push_back(s1[i] - '0');
}
for (int i = s2.length() - 1; i >= 0; i--) {
b.push_back(s2[i] - '0');
}
multi(a, b);
//倒过来输出
for (int i = a.size() - 1; i >= 0; i--) {
printf("%d", a[i]);
}
return 0;
}
快速傅里叶变换(FFT)
我不会
大整数相减
模拟竖式运算, a − b a-b a−b 如果 a > b a>b a>b 结果一定是个正数,不需要考虑太多。如果 b > a b>a b>a,就计算 b − a b-a b−a 然后对结果加负号。
#include <iostream>
#include <algorithm>
#include <vector>
using namespace std;
bool operator<(const string s1, const string s2) {//重载<运算符,用于判断输入的a和b哪个大。
if (s1.length() < s2.length())return true;
if (s1.length() > s2.length())return false;
if (s1.compare(s2) < 0)return true;
return false;
}
bool flag = false;
//进位
void carry(vector<int>& a) {
for (int i = 0; i < a.size(); i++) {
if (i != a.size() - 1 && a[i] < 0) {
a[i + 1] -= 1;
a[i] += 10;
}
}
//形如556-553,倒过来 - 最后前面会有两个0
while (a.size() > 1 && a[a.size() - 1] == 0)
a.pop_back();
}
// a = a - b
void subtract(vector<int>& a, vector<int>b) {
for (int i = 0; i < b.size(); i++) {
a[i] -= b[i];
}
carry(a);
}
int main() {
vector<int>a, b;
string s1, s2;
cin >> s1 >> s2;
if (s1 < s2) {
flag = true;
s1.swap(s2);
}
for (int i = s1.length() - 1; i >= 0; i--) {
a.push_back(s1[i] - '0');
}
for (int i = s2.length() - 1; i >= 0; i--) {
b.push_back(s2[i] - '0');
}
subtract(a, b);
//倒过来输出
if (flag)putchar('-');
for (int i = a.size() - 1; i >= 0; i--) {
printf("%d", a[i]);
}
return 0;
}
大整数除以小整数
小整数就是是可以用 int
或者 long long
表示出来的。
模拟竖式除法,这个是最好写的。
例题:https://www.luogu.com.cn/problem/P1480
#include <string>
#include <vector>
#include <iostream>
using namespace std;
string div(string a, int b = 2) {
int x = 0;
string res;
int i = 0;
for (; i < a.length(); i++) {
a[i] ^= 48;
x = x * 10 + a[i];
res.push_back(x / b + '0');
x %= b;
}
for (i = 0; res.length() > 1 && res[0] == 48; i++)
res.erase(res.begin(), res.begin() + 1); //去前导0
//x中为最后的余数
return res;
}
int main() {
int b;
string str;
cin >> str >> b;
cout << div(str, b);
return 0;
}