16.8 大數加法
第一個直式運算:加法。先看人類怎麼算 12345 + 678:
1 1 1 <- 滿十進上來的 1
1 2 3 4 5
+ 6 7 8
-----------
1 3 0 2 3
個位對齊,從右往左:5+8=13 寫 3 進 1;4+7+1=12 寫 2 進 1;3+6+1=10 寫 0 進 1;2+1=3;最前面的 1 照抄。翻成程式是四個步驟:
- 讀入並反向:兩個數字字串各自轉成 16.7 的反向陣列
A、B——個位對齊自動完成。 - 逐位相加:
C[i]收下A[i] + B[i](短的那個數沒有這一位就當 0——程式裡用兩個迴圈各自把自己的位加進去,最省事)。 - 統一進位:從低位往高位掃一遍,
C[i + 1] += C[i] / 10; C[i] %= 10;把每格壓回 0 \sim 9。一遍就夠——每格最多 9 + 9 = 18,收下前一格送來的進位也不超過 19,往上送的進位頂多是 1,不會雪球越滾越大。 - 去前導零、倒著印:答案陣列多開的高位格若沒用到會是 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;
}
執行結果(輸入 99 與 1):
100
幾個邊界值得親手驗一遍:不同長度(12345 加 678 得 13023)、一路連環進位(99 加 1——多開的那格真的用上了)、以及 0 加 0——答案是 0。
最後兩行防呆的細節值得放大看。C 多開了一格給「最高位再進位」用,沒用到時它是 0——不去掉就會印出 0100 這種東西;而去零迴圈裡的 C.size() > 1,是幫 0 + 0 這種答案保底:整個陣列都是 0 也得留一格,0 才印得出來。這一組「去前導零+至少留一位」是所有大數運算的固定收尾,下一節原封不動再用一次。
動手試試看:解掉大數加法——就是本節的程式。注意題目的位數上看 10^7,開頭那兩行輸入加速(12.10)別漏。