#P1571. 2023.05.06-暑期实习-第二题-加法
-
ID: 19
Tried: 189
Accepted: 23
Difficulty: 6
2023.05.06-暑期实习-第二题-加法
题目内容
塔子哥是一个喜欢数学的小学生,他经常在课余时间玩一些有趣的数学游戏。有一天,他收到了一封神秘的信件,信上写着一个两个数的加法表达式,这个表达式中不止有阿拉伯数字,还有可能包含小数点字符与特殊字符,特殊字符包括 !@#
。信上还附有一张纸条,上面写着特殊字符加法的规则。
信上还说,如果塔子哥能正确地计算出表达式的结果,并在三天内回信,就能得到一个惊喜的礼物。塔子哥才刚刚学会个位数加法,所以他现在有些没办法解决这个问题,但是他很想得到这份礼物,所以你能帮他解决这个问题吗?
特殊字符的加法运算规则如下:
!+!=0 !+@=13 !+#=4 @+@=7 @+#=20 #+#=5
注意: 题目保证输入表达式有解。
输入描述
输入第一行为一个整数 len ,表示加法表达式的长度。( 1≤len≤1000 )
输入第二行为一个长度为 len 的字符串,表示加法表达式。
输出描述
输出为一个整数 ans ,表示表达式的运算结果。(输出结果忽略前导零和后导零,并且当小数点后面为0时只需要输出整数)
样例一
输入
14
#9.@527+#4.!19
输出
64.3717
样例解释
由#+#=5
,@+!=13
,得列竖式有
#9.@527
+ #4.!19
——————————
64.3717
样例二
输入
13
9#.#95+9@.@65
输出
202.16
样例解释
由#+@=20
,得列竖式有
9#.#95
+ 9@.@65
————————
202.160
题面解释:塔子哥是一个喜欢数学的小学生,他在课余时间经常玩一些有趣的数学游戏。有一天,他收到了一个神秘的信件,信中写着一个包含阿拉伯数字、小数点和特殊字符的加法表达式。这些特殊字符包括 !
、@
和 #
,并且它们之间的加法运算规则如下:! + ! = 0
、! + @ = 13
、! + # = 4
、@ + @ = 7
、@ + # = 20
和 # + # = 5
。为了能够正确计算出这个表达式的结果并在三天内回信,塔子哥需要你的帮助。输入的第一行为一个整数 len
,表示加法表达式的长度(1≤len≤1000);第二行为一个长度为 len
的字符串,表示加法表达式。输出一个整数或小数 ans
,表示表达式的运算结果,输出结果应忽略前导零和后导零,并且当小数点后面为0时只需要输出整数部分。
题解
这道题目要求我们模拟两个数的加法,其中数的形式包含阿拉伯数字、小数点及特殊字符(!
、@
、#
)。由于这些特殊字符之间有特定的加法规则,我们需要根据这些规则进行处理。
解决思路
- 解析输入:首先读取表达式长度和表达式字符串,找到加号
+
的位置,将表达式分为两个部分。 - 处理小数点:为了方便后续计算,我们确保两个数都有小数部分。如果没有小数部分,则在后面加上
.0
。 - 分离整数部分和小数部分:分别提取出整数部分和小数部分。
- 模拟加法:
- 小数部分加法:从小数部分开始逐位相加,并处理进位。
- 整数部分加法:同样逐位相加,注意进位。
- 格式化输出:在输出结果时,去掉不必要的前导零和后导零,并根据规则决定是否输出小数点后面的零。
代码分析
这段代码实现了对包含阿拉伯数字、小数点和特殊字符(!
、@
、#
)的加法表达式的模拟加法运算。主要步骤包括:
-
变量定义:使用了
n
来存储输入的表达式长度,s
用于存储加法表达式,t
用于追踪进位。 -
特殊字符加法规则:
get
函数处理特殊字符之间的加法运算,根据字符组合返回相应的结果。 -
小数部分加法:
suf_add
函数从小数部分逐位加法,调用get
函数处理特殊字符,并在计算过程中管理进位。 -
整数部分加法:
pre_add
函数与小数部分类似,从整数部分进行逐位加法,并在结束后处理剩余的进位。 -
主函数逻辑:主函数读取输入,查找加号并分割字符串,确保每部分都有小数点,最后调用相应的加法函数计算结果,并格式化输出,去除多余的零。
Python代码
_ = input() # 读取输入长度,实际未使用
a, b = input().split('+') # 读取加法表达式,分割成两个部分 a 和 b
# 分割整数部分和小数部分
if '.' in a:
a_int, a_dec = a.split('.') # 如果 a 中有小数点,分割为整数和小数部分
else:
a_int, a_dec = a, '' # 如果没有小数点,小数部分为空
if '.' in b:
b_int, b_dec = b.split('.') # 如果 b 中有小数点,分割为整数和小数部分
else:
b_int, b_dec = b, '' # 如果没有小数点,小数部分为空
# 分别对齐整数部分和小数部分
if len(a_int) > len(b_int):
# 如果 a 的整数部分较长,则在 b 的整数部分前补零
b_int = '0' * (len(a_int) - len(b_int)) + b_int
else:
# 如果 b 的整数部分较长,则在 a 的整数部分前补零
a_int = '0' * (len(b_int) - len(a_int)) + a_int
# 对齐小数部分
if len(a_dec) > len(b_dec):
# 如果 a 的小数部分较长,则在 b 的小数部分后补零
b_dec = b_dec + '0' * (len(a_dec) - len(b_dec))
else:
# 如果 b 的小数部分较长,则在 a 的小数部分后补零
a_dec = a_dec + '0' * (len(b_dec) - len(a_dec))
# 定义一位的加法运算函数, carry为进位
def sym_add(x, y, carry):
# 定义特殊字符的加法规则
rule = {
'!!': 0,
'!@': 13, '@!': 13,
'!#': 4, '#!': 4,
'@@': 7,
'@#': 20, '#@': 20,
'##': 5
}
if x in '!@#': # 如果 x 是特殊字符
s = str(rule[x+y] + carry) # 根据规则计算加法结果
else:
s = str(int(x) + int(y) + carry) # 否则直接进行数字加法
# 如果结果只有一位,返回结果和进位0
if len(s) == 1:
return s, 0
else:
return s[1], int(s[0]) # 否则返回当前位和进位
# 计算小数部分
ans, carry = '', 0 # 初始化结果和进位
for i in reversed(range(len(a_dec))): # 从小数部分的最后一位开始计算
res, carry = sym_add(a_dec[i], b_dec[i], carry) # 进行加法运算
ans = res + ans # 结果前插入当前位
# 添加小数点
ans = '.' + ans # 在结果前添加小数点
# 计算整数部分
for i in reversed(range(len(a_int))): # 从整数部分的最后一位开始计算
res, carry = sym_add(a_int[i], b_int[i], carry) # 进行加法运算
ans = res + ans # 结果前插入当前位
if carry != 0: # 如果最后还有进位,添加到结果前面
ans = str(carry) + ans
# 去除前导0和后导0
ans = ans.strip('0') # 去除结果中的前导0
# 有可能整数部分只有0, 去除前导0后就没有整数部分了, 因此要补一个0
if ans[0] == '.': # 如果结果以小数点开头
ans = '0' + ans # 在前面补零
# 如果没有小数部分, 那么小数点也应该去掉
if ans[-1] == '.': # 如果结果以小数点结尾
ans = ans[:-1] # 去掉小数点
print(ans) # 输出最终结果
Java
import java.util.ArrayList;
import java.util.Scanner;
public class Main {
public static ArrayList<Character> A = new ArrayList<>(), B = new ArrayList<>(), res = new ArrayList<>();
public static ArrayList<Character> A1 = new ArrayList<>(), B1 = new ArrayList<>(), res1 = new ArrayList<>();
static int flag = 0;
public static void main(String[] args) {
Scanner in = new Scanner(System.in);
int n = in.nextInt();
String s = in.next();
String[] split = s.split("\\+");
String a = split[0];
String b = split[1];
if(a.contains(".") && b.contains(".")){
String[] sa = a.split("\\.");
String[] sb = b.split("\\.");
String s1 = sa[1].length() >= sb[1].length() ? sa[1] : sb[1];
String s2 = sa[1].length() >= sb[1].length() ? sb[1] : sa[1];
int x = s1.length()-s2.length();
for(int i=1;i<=x;i++){
s2 = s2 + "0";
}
for (int i = s1.length()-1; i >= 0; i--){
A1.add(s1.charAt(i));
B1.add(s2.charAt(i));
}
add1();
s1 = sa[0].length() >= sb[0].length() ? sa[0] : sb[0];
s2 = sa[0].length() >= sb[0].length() ? sb[0] : sa[0];
//这里就是反着装进list
for (int i = s1.length()-1; i >= 0; i--) A.add(s1.charAt(i));
for (int i = s2.length()-1; i >= 0; i--) B.add(s2.charAt(i));
add();
}else if(a.contains(".")){
String[] sa = a.split("\\.");
String s1 = sa[0].length() >= b.length() ? sa[0] : b;
String s2 = sa[0].length() >= b.length() ? b : sa[0];
//这里就是反着装进list
for (int i = s1.length()-1; i >= 0; i--) A.add(s1.charAt(i));
for (int i = s2.length()-1; i >= 0; i--) B.add(s2.charAt(i));
add();
for (int i = sa[1].length()-1; i >=0; i--) {
res1.add(sa[1].charAt(i));
}
}else if(b.contains(".")){
String[] sa = b.split("\\.");
String s1 = sa[0].length() >= a.length() ? sa[0] : a;
String s2 = sa[0].length() >= a.length() ? a : sa[0];
//这里就是反着装进list
for (int i = s1.length()-1; i >= 0; i--) A.add(s1.charAt(i));
for (int i = s2.length()-1; i >= 0; i--) B.add(s2.charAt(i));
add();
for (int i = sa[1].length()-1; i >=0; i--) {
res1.add(sa[1].charAt(i));
}
}else{
for (int i = a.length()-1; i >= 0; i--) A.add(a.charAt(i));
for (int i = b.length()-1; i >= 0; i--) B.add(b.charAt(i));
add();
}
// 先算小数
StringBuilder string = new StringBuilder();
//因为是反着装进list的,输出就得从末尾开始输出,例如751->157
// for (int i = res.size()-1; i>=0; i--) System.out.print(res.get(i));
// System.out.print(".");
// for (int i = res1.size()-1; i>=0; i--) System.out.print(res1.get(i));
for (int i = res.size()-1; i>=0; i--){
string.append(res.get(i));
}
string.append('.');
for (int i = res1.size()-1; i>=0; i--){
string.append(res1.get(i));
}
while(string.charAt(string.length()-1)=='0'){
string.deleteCharAt(string.length()-1);
}
if(string.charAt(string.length()-1)=='.')
string.deleteCharAt(string.length()-1);
while(string.charAt(0)=='0'&&string.length()>1){
string.deleteCharAt(0);
}
System.out.println(string.toString());
}
//这里就是最基础的十进制加法,将A和B的当前位加在一起,需要进位就靠t/=10来判断进位
public static void add() {
//如果之前一位进位了,那么t就是1,没进位t就是0
int t = flag;
for (int i = 0; i < B.size(); i++) {
char num1 = A.get(i);
char num2 = B.get(i);
if(num1>='0'&&num1<='9'&&num2>='0'&&num2<='9')t+=num2-'0'+num1-'0';
else if(num1=='!'&&num2=='!')t=t;
else if(num1=='!'&&num2=='@'||num1=='@'&&num2=='!')t+=13;
else if(num1=='!'&&num2=='#'||num1=='#'&&num2=='!')t+=4;
else if(num1=='@'&&num2=='#'||num1=='#'&&num2=='@')t+=20;
else if(num1=='@'&&num2=='@')t+=7;
else if(num1=='#'&&num2=='#')t+=5;
res.add( (char)((t % 10) + '0') );
t /= 10;
}
for (int i = B.size(); i < A.size(); i++) {
int num= A.get(i);
t = num - '0' + t;
res.add((char)((t % 10) + '0'));
t /= 10;
}
//特判,如果a和b长度一样且a的最高位加b的最高位进位的话,t还保留着进位信息,加到res就好
if (t > 0) res.add((char)(t+'0'));
}
public static void add1() {
//如果之前一位进位了,那么t就是1,没进位t就是0
int t = 0;
for (int i = 0; i < A1.size(); i++) {
char num1 = A1.get(i);
char num2 = B1.get(i);
if(num1>='0'&&num1<='9'&&num2>='0'&&num2<='9')t+=num2-'0'+num1-'0';
else if(num1=='!'&&num2=='!')t=t;
else if(num1=='!'&&num2=='@'||num1=='@'&&num2=='!')t+=13;
else if(num1=='!'&&num2=='#'||num1=='#'&&num2=='!')t+=4;
else if(num1=='@'&&num2=='#'||num1=='#'&&num2=='@')t+=20;
else if(num1=='@'&&num2=='@')t+=7;
else if(num1=='#'&&num2=='#')t+=5;
res1.add((char)((t % 10) + '0') );
t /= 10;
}
//特判,如果a和b长度一样且a的最高位加b的最高位进位的话,t还保留着进位信息,加到res就好
if (t > 0) flag = t;
}
}
Cpp
#include <iostream>
#include <algorithm>
#include <cstdio>
#include <cstring>
#define x first
#define y second
using namespace std;
typedef long long ll;
typedef pair<int, int> pii;
const int N = 1010;
int n; // 表达式的长度
string s; // 存储输入的加法表达式
int t = 0; // 全局变量,记录进位
// 处理特殊字符的加法运算
int get(char a, char b) {
if (a == '!' && b == '!') return 0;
else if (a == '!' && b == '@') return 13;
else if (a == '!' && b == '#') return 4;
else if (a == '@' && b == '@') return 7;
else if (a == '@' && b == '#') return 20;
else if (a == '#' && b == '#') return 5;
return get(b, a); // 如果找不到规则,调换字符顺序
}
// 小数部分加法
string suf_add(string a, string b) {
string res = ""; // 存储结果
for (int i = max(a.size(), b.size()) - 1; ~i; i--) {
char c = '0', d = '0'; // 默认字符为'0'
if (i < (int)a.size()) c = a[i]; // 取出当前位
if (i < (int)b.size()) d = b[i]; // 取出当前位
// 判断是否是特殊字符并进行加法计算
if (!isdigit(c) && !isdigit(d)) t += get(c, d);
else t += c - '0' + d - '0'; // 阿拉伯数字相加
res += to_string(t % 10); // 结果的当前位
t /= 10; // 更新进位
}
reverse(res.begin(), res.end()); // 反转结果
while (res.size() > 1 && res.back() == '0') res.pop_back(); // 去除末尾零
return res;
}
// 整数部分加法
string pre_add(string a, string b) {
reverse(a.begin(), a.end()); // 反转字符串以便从低位开始加
reverse(b.begin(), b.end());
string res = "";
for (int i = 0; i < (int)a.size() || i < (int)b.size(); i++) {
char c = '0', d = '0';
if (i < (int)a.size()) c = a[i]; // 取出当前位
if (i < (int)b.size()) d = b[i]; // 取出当前位
// 判断是否是特殊字符并进行加法计算
if (!isdigit(c) && !isdigit(d)) t += get(c, d);
else t += c - '0' + d - '0';
res += to_string(t % 10); // 结果的当前位
t /= 10; // 更新进位
}
// 处理最后的进位
while (t) {
res += to_string(t % 10);
t /= 10;
}
while (res.size() > 1 && res.back() == '0') res.pop_back(); // 去除末尾零
reverse(res.begin(), res.end()); // 反转回原来的顺序
return res;
}
int main() {
cin >> n >> s; // 输入长度和表达式
int pos = s.find('+'); // 查找加号的位置
string a = s.substr(0, pos), b = s.substr(pos + 1); // 分割成两部分
if (a.find('.') == a.npos) a += ".0"; // 若没有小数点,加上.0
if (b.find('.') == b.npos) b += ".0"; // 若没有小数点,加上.0
int pos_a = a.find('.'), pos_b = b.find('.'); // 找到小数点的位置
string pre_a = a.substr(0, pos_a), suf_a = a.substr(pos_a + 1); // 分离整数和小数部分
string pre_b = b.substr(0, pos_b), suf_b = b.substr(pos_b + 1);
// 先计算小数部分,再计算整数部分
string suf_res = suf_add(suf_a, suf_b);
string pre_res = pre_add(pre_a, pre_b);
// 输出结果
cout << pre_res;
if (suf_res != "0") cout << '.' << suf_res; // 如果小数部分不为零,则输出
return 0;
}
本题属于以下题库,请选择所需题库进行购买