题目概况
链接: https://nanti.jisuanke.com/t/T1180
难度: 普及/提高-(计蒜客评级普及T3)
题目分析
简化题目: 给定两个实数,相加即可
**涉及知识点:**高精度加高精度算法以及一些玄学操作
解题思路:
私以为,这道题本质上就是考察PJT2难度的 高精度算法另加一些 小数的处理,故我们重点讨论如何处理小数:
1. 关于相加,我们可以考虑 去掉小数点直接当成两个高精度整数相加。但如果数据1.999+0.1
会被当做1999+1
,结果并不符合我们的预期,所以需要在加之前 在后面补0(倒着存肯定就倒着补呗)
2. 关于小数点,我们在相加时去掉了,但输出还是需要的,所以要 记录一下小数点位置。输出小数点的位置应该满足总长度-i-1 == 小数点位置
。
3. 处理小数部分多余的0,用while
循环从第0
位起找到 第一个非0位
4. (这一点是突发奇想的,在本题中不会有这种情况)如果我们出现了999.99900 + 0.001000
这种情况,相加得1000
,但由于处理多余0会结果会变成1,,我们可以尝试把这种情况给处理一下
代码要点拆解
一、数据准备
const int MAXN = 205;
//100位整数+小数,加起来就是200位,数组要开到200位以上
int len1, len2;
//记录去掉小数点后的长度
int dot_idx, dot_idx1, dot_idx2;
//记录长一点的小数点位置;加数的小数点位置;另一个加数的小数点位置(补0时用得上)
int a[MAXN], b[MAXN];
//两个加数(相加用)
string s1, s2;
//两个加数(输入进来的)
string ss1, ss2;
//两个加数(去掉小数点的)
二、寻找小数点
直接用find
函数
//寻找小数点
dot_idx1 = s1.find('.');
dot_idx2 = s2.find('.');
dot_idx = max(dot_idx1, dot_idx2);
三、补0
//补0
//n1,n2分别是第一(二)个字符串减去第二(一)的字符串的小数位数得到的差
int n1 = (s1.size() - dot_idx1 - 1) - (s2.size() - dot_idx2 - 1);
int n2 = (s2.size() - dot_idx2 - 1) - (s1.size() - dot_idx1 - 1);
//如果n1>n2,那就是s2的小数部分要补
if (n1 > n2) {
while (n1--) {
s2 += '0';
}
} else if (n2 > n1) { //反之就是s1的小数部分要补
while (n2--) {
s1 += '0';
}
}
//如果相等就不用补
四、去除小数点
碰到小数点就continue
,否则就加上
for (int i = 0; i < s1.size(); i++) {
if (s1[i] == '.') continue;
else ss1 += s1[i];
}
for (int i = 0; i < s2.size(); i++) {
if (s2[i] == '.') continue;
else ss2 += s2[i];
}
五、常规高精度
相信能做到这道题高精度算法你已经敲得滚瓜烂熟了,至于在处理长度变化时加了dot_idx++
,是因为如果我们的长度加了,那么小数点位就得后移
比如:
在出现999.99900 + 0.001000
的情况时,本来我们的小数点位是3
,但是最后结果是1000
,因为进位了,所以我们要把小数点位后移至4
//常规高精度
len1 = max(len1, len2);
//相加
for (int i = 0; i < len1; i++) {
a[i] += b[i];
}
//进位
for (int i = 0; i < len1; i++) {
a[i + 1] += a[i] / 10;
a[i] %= 10;
}
//进位引起的长度变化
while (a[len1]) {
a[len1 + 1] += a[len1] / 10;
a[len1] %= 10;
len1++;
dot_idx++;
}
六、处理后缀0
两个判断条件:
1. 当前位也就是a[ending]
还是0
2. 当前位没有越过小数点位
//处理后缀0
int ending = 0;
while (a[ending] == 0 && ending <= len1 - dot_idx - 1) {
ending++;
}
七、输出操作
与一般地不同,我们只能输出到小数部分的第一个非零位,也就是ending
for (int i = len1 - 1; i >= ending; i--) {
if (len1 - i - 1 == dot_idx) { //解题思路中讲了
cout << '.';
}
cout << a[i];
}
完整代码
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int MAXN = 205;
int len1, len2;
int dot_idx, dot_idx1, dot_idx2;
int a[MAXN], b[MAXN];
string s1, s2;
string ss1, ss2;
int main() {
cin >> s1 >> s2;
//寻找小数点
dot_idx1 = s1.find('.');
dot_idx2 = s2.find('.');
dot_idx = max(dot_idx1, dot_idx2);
//补0
int n1 = (s1.size() - dot_idx1 - 1) - (s2.size() - dot_idx2 - 1);
int n2 = (s2.size() - dot_idx2 - 1) - (s1.size() - dot_idx1 - 1);
if (n1 > n2) {
while (n1--) {
s2 += '0';
}
} else if (n2 > n1) {
while (n2--) {
s1 += '0';
}
}
//去除小数点
for (int i = 0; i < s1.size(); i++) {
if (s1[i] == '.') continue;
else ss1 += s1[i];
}
for (int i = 0; i < s2.size(); i++) {
if (s2[i] == '.') continue;
else ss2 += s2[i];
}
//存储
len1 = ss1.size(), len2 = ss2.size();
for (int i = 0; i < len1; i++) {
a[i] = ss1[len1 - i - 1] - '0';
}
for (int i = 0; i < len2; i++) {
b[i] = ss2[len2 - i - 1] - '0';
}
//常规高精度
len1 = max(len1, len2);
for (int i = 0; i < len1; i++) {
a[i] += b[i];
}
for (int i = 0; i < len1; i++) {
a[i + 1] += a[i] / 10;
a[i] %= 10;
}
while (a[len1]) {
a[len1 + 1] += a[len1] / 10;
a[len1] %= 10;
len1++;
dot_idx++;
}
//处理后缀0
int ending = 0;
while (a[ending] == 0 && ending <= len1 - dot_idx - 1) {
ending++;
}
//输出
for (int i = len1 - 1; i >= ending; i--) {
if (len1 - i - 1 == dot_idx) {
cout << '.';
}
cout << a[i];
}
return 0;
}