题解:洛谷 P2142 高精度减法
【题目来源】
【题目描述】
高精度减法。
【输入】
两个整数 \(a,b\)(第二个可能比第一个大)。
【输出】
结果(是负数要输出负号)。
【输入样例】
2
1
【输出样例】
1
【核心思想】
-
问题分析:给定两个可能非常大的整数 \(a\) 和 \(b\)(长度可达 \(10^5\) 位),需要计算它们的差 \(a - b\)。由于整数大小远超标准数据类型范围,必须使用高精度算法模拟手算减法过程:从最低位开始逐位相减,若被减数当前位小于减数当前位,则向高位借位。同时需判断结果符号,若 \(a < b\) 则输出负号。
-
算法选择:
- 字符串转逆序数组:将两个整数以字符串形式读入,按位逆序存入整型数组(或 vector),使个位对齐在索引 0 处,便于从低位向高位计算。
- 大小比较:先比较两数绝对值大小,若 \(a < b\) 则交换 \(a\) 和 \(b\) 并标记负号,保证计算时始终用大数减小数。
- 逐位减法 + 借位:遍历每一位,被减数当前位减去减数当前位及上次借位,若结果为负则加 10 并标记借位 1,否则借位为 0。
- 去除前导零:结果数组高位可能有多余的 0,需删除(但至少保留一位,避免输出空串)。
-
关键步骤(以 vector 实现为例):
- 读入与预处理:读取字符串 \(a\)、\(b\),逆序存入
vector<int> A, B,其中A[0]为个位。 - 比较大小:若
A.size() != B.size(),长度长的大;长度相同则从高位向低位比较。 - 交换与符号标记:若
A < B,则交换A和B,并标记负号。 - 逐位减法:循环
i从 0 到A.size()-1:- 令
t = A[i] - 借位(借位初始为 0)。 - 若
i < B.size(),则t -= B[i]。 - 若
t < 0,则t += 10,借位设为 1;否则借位设为 0。 - 将
t存入结果数组C。
- 令
- 去除前导零:移除
C末尾多余的 0,但若C为空则保留一个 0。 - 输出:若有负号先输出
-,再逆序输出C。
- 读入与预处理:读取字符串 \(a\)、\(b\),逆序存入
-
时间/空间复杂度:
- 时间复杂度:\(O(\max(|a|, |b|))\),逐位遍历一次。
- 空间复杂度:\(O(\max(|a|, |b|))\),存储三个数组。
-
高精度模拟的核心思想:
- 逆序存储:将数字字符串反转后存入数组,使最低位对齐,方便从低位开始逐位计算。
- 借位处理:模拟手算减法中的“借位”操作,通过
t < 0判断是否需要借位,并更新借位标志。 - 符号判断:先比较绝对值大小,若被减数较小则交换并输出负号,避免处理负数减法(\(a-b = -(b-a)\))。
- 前导零去除:高位多余的零不影响数值,需在输出前清理,但保留至少一位数字。
- 适用于所有大整数加减法、乘法等运算,是算法竞赛中处理超大整数的标准方法。
【算法标签】
普及- #高精度
【代码详解】
#include <bits/stdc++.h>
using namespace std;
const int N = 100005; // 定义数组的最大长度
int A[N], B[N], C[N]; // A和B用于存储输入的两个大数,C用于存储结果
int la, lb, lc; // la, lb分别表示A和B的长度,lc表示结果C的长度
string a, b; // 用于存储输入的两个大数的字符串形式
// 比较函数,用于比较两个大数的大小
bool cmp(int A[], int B[])
{
if (la != lb) return la > lb; // 如果长度不同,直接比较长度
for (int i = la - 1; i >= 0; i--) // 从最高位开始逐位比较
if (A[i] != B[i])
return A[i] > B[i]; // 如果某一位不同,返回比较结果
return true; // 如果所有位都相同,返回true,避免结果为-0
}
// 大数减法函数,计算A - B,结果存储在C中
void sub(int A[], int B[], int C[])
{
for (int i = 0; i < lc; i++) {
if (A[i] < B[i]) {
A[i + 1]--; // 借位
A[i] += 10; // 当前位加10
}
C[i] = A[i] - B[i]; // 计算当前位的差值并存储在C中
}
while (lc && C[lc] == 0) lc--; // 去掉结果中的前导0
}
int main()
{
cin >> a >> b; // 输入两个大数
la = a.size(), lb = b.size(), lc = max(la, lb); // 计算长度
// 将字符串形式的大数转换为数组形式,并逆序存储(方便计算)
for (int i = la - 1; i >= 0; i--)
A[la - 1 - i] = a[i] - '0';
for (int i = lb - 1; i >= 0; i--)
B[lb - 1 - i] = b[i] - '0';
// 如果A小于B,交换A和B,并输出负号
if (!cmp(A, B)) {
swap(A, B);
cout << '-';
}
// 执行减法操作
sub(A, B, C);
// 输出结果
for (int i = lc; i >= 0; i--)
cout << C[i];
return 0;
}
#include <bits/stdc++.h>
using namespace std;
string a, b; // 用于存储输入的两个大数的字符串形式
vector<int> A, B; // 使用vector存储大数的每一位,方便动态调整大小
// 比较函数,用于比较两个大数的大小
bool cmp(vector<int> &A, vector<int> &B)
{
if (A.size() != B.size()) return A.size() > B.size(); // 如果长度不同,直接比较长度
for (int i = A.size() - 1; i >= 0; i--) // 从最高位开始逐位比较
if (A[i] != B[i])
return A[i] > B[i]; // 如果某一位不同,返回比较结果
return true; // 如果所有位都相同,返回true
}
// 大数减法函数,计算 A - B,结果存储在 C 中
vector<int> sub(vector<int> &A, vector<int> &B)
{
vector<int> C; // 用于存储结果
for (int i = 0, t = 0; i < A.size(); i++) // t 表示借位
{
t = A[i] - t; // 先减去借位
if (i < B.size()) t -= B[i]; // 如果 B 还有位数,减去 B 的当前位
C.push_back((t + 10) % 10); // 将当前位的差值存入 C(处理负数情况)
if (t < 0) t = 1; // 如果差值为负,需要借位
else t = 0; // 否则不需要借位
}
while (C.size() > 1 && C.back() == 0) C.pop_back(); // 去掉结果中的前导0
return C;
}
int main()
{
cin >> a >> b; // 输入两个大数
// 将字符串形式的大数转换为vector,并逆序存储(方便计算)
for (int i = a.size() - 1; i >= 0; i--) A.push_back(a[i] - '0');
for (int i = b.size() - 1; i >= 0; i--) B.push_back(b[i] - '0');
// 比较 A 和 B 的大小
if (cmp(A, B)) // 如果 A >= B
{
auto C = sub(A, B); // 计算 A - B
for (int i = C.size() - 1; i >= 0; i--) cout << C[i]; // 输出结果
}
else // 如果 A < B
{
auto C = sub(B, A); // 计算 B - A
cout << "-"; // 输出负号
for (int i = C.size() - 1; i >= 0; i--) cout << C[i]; // 输出结果
}
return 0;
}
【运行结果】
2
1
1
浙公网安备 33010602011771号