16.8 大數加法

第一個直式運算:加法。先看人類怎麼算 12345 + 678

      1 1 1        <- 滿十進上來的 1
    1 2 3 4 5
  +     6 7 8
  -----------
    1 3 0 2 3

個位對齊,從右往左:5+8=13314+7+1=12213+6+1=10012+1=3;最前面的 1 照抄。翻成程式是四個步驟:

  1. 讀入並反向:兩個數字字串各自轉成 16.7 的反向陣列 AB——個位對齊自動完成。
  2. 逐位相加C[i] 收下 A[i] + B[i](短的那個數沒有這一位就當 0——程式裡用兩個迴圈各自把自己的位加進去,最省事)。
  3. 統一進位:從低位往高位掃一遍,C[i + 1] += C[i] / 10; C[i] %= 10; 把每格壓回 0 \sim 9。一遍就夠——每格最多 9 + 9 = 18,收下前一格送來的進位也不超過 19,往上送的進位頂多是 1,不會雪球越滾越大。
  4. 去前導零、倒著印:答案陣列多開的高位格若沒用到會是 0,印之前要拿掉;高位住尾端,輸出要從尾端倒著印。

完整程式

#include <iostream>
#include <string>
#include <vector>
using namespace std;

// 把字串形式的非負整數,轉成「個位住在索引 0」的數字陣列(16.7)
vector<int> toDigits(const string& s) {
    int len = s.size();
    vector<int> d(len);
    for (int i = 0; i < len; i++) {
        d[i] = s[len - 1 - i] - '0';
    }
    return d;
}

int main() {
    ios::sync_with_stdio(false);          // 輸入可能長達千萬個字元:加速(12.10)
    cin.tie(nullptr);

    string a, b;
    cin >> a >> b;
    vector<int> A = toDigits(a);
    vector<int> B = toDigits(b);

    int n = max(A.size(), B.size());      // 答案至少這麼多位
    vector<int> C(n + 1, 0);              // 多開一格:最高位可能再進一位
    for (int i = 0; i < (int)A.size(); i++) C[i] += A[i];
    for (int i = 0; i < (int)B.size(); i++) C[i] += B[i];

    for (int i = 0; i < n; i++) {         // 統一進位:把每一格壓回 0~9
        C[i + 1] += C[i] / 10;
        C[i] %= 10;
    }

    while (C.size() > 1 && C.back() == 0) C.pop_back();   // 去前導零:沒用到的高位拿掉

    for (int i = (int)C.size() - 1; i >= 0; i--) cout << C[i];   // 高位在尾端:倒著印
    cout << '\n';
    return 0;
}

執行結果(輸入 991):

100

幾個邊界值得親手驗一遍:不同長度(1234567813023)、一路連環進位(991——多開的那格真的用上了)、以及 00——答案是 0

最後兩行防呆的細節值得放大看。C 多開了一格給「最高位再進位」用,沒用到時它是 0——不去掉就會印出 0100 這種東西;而去零迴圈裡的 C.size() > 1,是幫 0 + 0 這種答案保底:整個陣列都是 0 也得留一格,0 才印得出來。這一組「去前導零+至少留一位」是所有大數運算的固定收尾,下一節原封不動再用一次。

動手試試看:解掉大數加法——就是本節的程式。注意題目的位數上看 10^7,開頭那兩行輸入加速(12.10)別漏。