《信息学奥赛一本通》第166题:大整数加法
求两个不超过200位的非负整数的和。
输入:
有两行,每行是一个不超过200位的非负整数,可能有多余的前导0。
输出:
一行,即相加后的结果。结果里不能有多余的前导0,即如果结果是342,那么就不能输
出为0342。
样例输入:
22222222222222222222
33333333333333333333
样例输出:
55555555555555555555
大家好,我是莫小特。
这篇文章给大家带来《信息学奥赛一本通》中的第 166题:大整数加法。
一、题意分析
本题要求对两个不超过 200 位 的非负整数进行加法运算。由于数值范围远超 long long
的表示能力,因此我们不能使用普通整数类型,而应当使用字符数组 + 整数数组的方式来模拟手工加法。
输入两个不超过 200 位的非负整数,输出它们的和。
不能使用普通整型运算,而要自己用数组来模拟加法的全过程。
所以使用 char
数组 al
、bl
来存放输入的两个大数。
由于 cin
不能直接读超长整数,这里用 gets()
读入整行字符串(虽然不安全,但在竞赛中常用来快速处理输入)。
读入后,通过:
cpp
la = strlen(al);
lb = strlen(bl);
得到两个字符串的长度,即数字的位数。
程序将字符串反向存入整数数组:
cpp
for (int i = 0; i <= la - 1; ++i)
{
a[la - i - 1] = al[i] - 48;
}
举个例子,输入 "123"
→ 存入数组后顺序为 a[0]=3, a[1]=2, a[2]=1
,即数组中低位在前,高位在后,便于从个位开始做加法。
这样设计的好处是:
可以像我们在纸上竖式加法那样,从个位向高位逐位累加。
二、完成代码
(1)定义变量
cpp
int a[2000], b[2000], c[2000];
char al[2000], bl[2000];
三个整数数组分别存放第一个数、第二个数及结果。
两个字符数组存放输入的字符串。
(2)处理输入
cpp
gets(al); gets(bl);
la = strlen(al);
lb = strlen(bl);
此处 gets
读入字符串,strlen
计算其长度。
(3)反向存储
cpp
for (int i = 0; i <= la - 1; ++i)
a[la - i - 1] = al[i] - 48;
for (int i = 0; i <= lb - 1; ++i)
b[lb - i - 1] = bl[i] - 48;
这里的 -48
是因为字符 '0'
的 ASCII 值为 48,
减去 48 就可以把字符转为真正的数字。
(4)按位相加
cpp
lc = 1; // 从个位开始
while (lc <= la || lc <= lb)
{
c[lc] = a[lc] + b[lc] + x; // 当前位相加
x = c[lc] / 10; // 判断是否产生进位
c[lc] %= 10; // 当前位只保留个位
lc++; // 移动到下一位
}
这里模拟了"手算加法"的核心步骤:
- 两数同位相加;
- 加上进位
x
; - 计算新的进位;
- 再继续下一位。
(5)处理最后进位
c[lc] = x;
如果最后一位相加后仍然有进位,就写入结果的最高位。
(6)去掉前导零
cpp
`while (c[lc] == 0 && lc > 1) lc--;`
防止输出类似 000123
的格式。只保留最左边的有效位。
(7)倒序输出
cpp
for (int i = lc; i >= 1; --i)
cout << c[i];
cout << endl;
因为之前是反向存储的,现在倒序输出即可得到正确顺序。
按照样例输入对数据进行验证。
符合样例输出,到网站提交测评。
测试通过!
三、完整代码
该题的完整代码如下:
cpp
#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
int a[2000], b[2000], c[2000];
char al[2000], bl[2000];
int main()
{
int la, lb, lc, x = 0;
gets(al); gets(bl); // 读入两个超长整数
la = strlen(al); lb = strlen(bl); // 获取两数长度
for (int i = 0; i <= la - 1; ++i) a[la - i - 1] = al[i] - 48;
for (int i = 0; i <= lb - 1; ++i) b[lb - i - 1] = bl[i] - 48;
lc = 1; // 从个位开始
while (lc <= la || lc <= lb) // 逐位相加
{
c[lc] = a[lc] + b[lc] + x; // 加上进位
x = c[lc] / 10; // 计算新的进位
c[lc] %= 10; // 当前位取个位
lc++; // 移动到下一位
}
c[lc] = x; // 处理最后的进位
while (c[lc] == 0 && lc > 1) lc--; // 去掉前导零
for (int i = lc; i >= 1; --i) cout << c[i];
cout << endl;
return 0;
}
四、总结
这道大整数加法题重点考察的是:
1、大数表示方法:用数组按位存储大数,每个数组元素保存一位(0~9),这样规避了语言内建数值范围的限制。
2、字符串与数字互转 :输入为字符串,需要把字符 '0'
~'9'
转换为整数 0~9,以便按位运算。
3、按位加法与进位处理:按位相加并保存进位是核心逻辑,循环要覆盖两个数长度的最大值,最后别忘了处理最高位的进位。
4、输出时去掉前导 0:相加结果不能包含多余的前导 0,输出前需确定最高非零位的位置。
5、代码规范与安全 :建议使用 string
、cin
/getline
代替 gets
,并保持数组下标约定的一致性与可读性。
这类题目是学习数组、字符串和基础算法思想的经典训练题,熟练掌握对今后处理大数问题(乘法、减法、进位/借位问题等)非常有帮助。
---end---
如果你觉得这篇文章对你有帮助,欢迎点赞、收藏、关注我哦!
如果有更好的方法也可以在评论区评论哦,我都会看哒~
我们下集见~