统一模型
输入统一用字符串;加、减、乘转成逆序数组,除法直接从字符串最高位向最低位扫描。本课不使用 vector。
只用字符串、普通数组和长度变量,学会把小学竖式准确翻译成程序。
输入统一用字符串;加、减、乘转成逆序数组,除法直接从字符串最高位向最低位扫描。本课不使用 vector。
每一步只保留本位数字,以及传给下一位的进位、借位或余数。算完的数字已经确定,不需要重算。
像小学竖式:从个位开始逐位相加,满 10 向前进 1。点“下一位”看进位怎么传。
#include <bits/stdc++.h>
using namespace std;
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
string A, B;
cin >> A >> B;
const int N = 100005;
static int a[N], b[N], c[N];
int lenA = A.size(), lenB = B.size();
for (int i = 0; i < lenA; i++) a[i] = A[lenA - 1 - i] - '0';
for (int i = 0; i < lenB; i++) b[i] = B[lenB - 1 - i] - '0';
int carry = 0;
int lenC = max(lenA, lenB);
for (int i = 0; i < lenC; i++) {
int sum = a[i] + b[i] + carry;
c[i] = sum % 10;
carry = sum / 10;
}
if (carry > 0) c[lenC++] = carry;
for (int i = lenC - 1; i >= 0; i--) cout << c[i];
cout << '\n';
return 0;
}
先读懂三个变量:c[i] 是本位答案,carry 是传给高位的进位,lenC 是答案有效位数。
a[i] 是 10i 位,b[j] 是 10j 位。两个位置相乘时,位权也相乘,所以指数相加:
一句话记住:数字相乘,下标相加。
| 23 中的数字 | 45 中的数字 | 实际贡献 | 累加位置 |
|---|---|---|---|
| 3 个一 | 5 个一 | 15 | c[0] += 15 |
| 3 个一 | 4 个十 | 120 | c[1] += 12 |
| 2 个十 | 5 个一 | 100 | c[1] += 10 |
| 2 个十 | 4 个十 | 800 | c[2] += 8 |
先收集四份乘积,再统一进位。注意 c[1] 会收到两份贡献,因此必须使用 +=。
// 第一阶段:收集所有乘积
for (int i = 0; i < lenA; i++)
for (int j = 0; j < lenB; j++)
c[i + j] += a[i] * b[j];
// 第二阶段:从低位向高位统一进位
for (int i = 0; i < lenA + lenB - 1; i++) {
c[i + 1] += c[i] / 10;
c[i] %= 10;
}
商的最高位由被除数最左边的数字决定,因此除法必须从左向右扫描。处理完前面的数字后,已经写出的商不会再变,只要保留余数继续计算。
例如处理完 123 ÷ 12 后,商是 10、余数是 3。再落下 4,当前数就是 3×10+4=34。
每次落下一位,只维护当前数、已经确定的商和余数。
long long remainder = 0;
bool started = false;
for (int i = 0; i < (int)A.size(); i++) {
remainder = remainder * 10 + (A[i] - '0');
int quotientDigit = remainder / divisor;
remainder %= divisor;
// 跳过前导零,但商中间的 0 必须输出
if (quotientDigit != 0 || started) {
cout << quotientDigit;
started = true;
}
}
if (!started) cout << 0;
started 为假时可以跳过商的前导零;一旦开始输出,后面算出的 0 也必须保留。
加、减、乘从个位走向高位,分别维护进位、借位和乘积;除法从最高位走向个位,维护余数。输出前都要处理前导零,结果为 0 时必须输出一个 0。