Level 2 · 算法入门

模拟与高精度

只用字符串、普通数组和长度变量,学会把小学竖式准确翻译成程序。

一、先安排课堂主线

统一模型

输入统一用字符串;加、减、乘转成逆序数组,除法直接从字符串最高位向最低位扫描。本课不使用 vector

维护与压缩

每一步只保留本位数字,以及传给下一位的进位、借位或余数。算完的数字已经确定,不需要重算。

二、互动演示:高精度加法 168 + 875

像小学竖式:从个位开始逐位相加,满 10 向前进 1。点“下一位”看进位怎么传。

三、C++14 高精度加法模板

#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 是答案有效位数。

四、乘法为什么写 c[i+j]

a[i]10i 位,b[j]10j 位。两个位置相乘时,位权也相乘,所以指数相加:

(a[i] × 10i) × (b[j] × 10j) = a[i]b[j] × 10i+j

一句话记住:数字相乘,下标相加。

23 中的数字45 中的数字实际贡献累加位置
3 个一5 个一15c[0] += 15
3 个一4 个十120c[1] += 12
2 个十5 个一100c[1] += 10
2 个十4 个十800c[2] += 8

互动演示:23 × 45

先收集四份乘积,再统一进位。注意 c[1] 会收到两份贡献,因此必须使用 +=

当前动作准备开始
落到哪里
数组 c(逆序)[0, 0, 0, 0]
// 第一阶段:收集所有乘积
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;
}

五、除法为什么从最高位开始

商的最高位由被除数最左边的数字决定,因此除法必须从左向右扫描。处理完前面的数字后,已经写出的商不会再变,只要保留余数继续计算。

当前数 = 上一步余数 × 10 + 新落下的一位

例如处理完 123 ÷ 12 后,商是 10、余数是 3。再落下 4,当前数就是 3×10+4=34

互动演示:1234 ÷ 12

每次落下一位,只维护当前数、已经确定的商和余数。

落下数字 / 当前数准备开始
商的有效部分
新余数0
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