CF952F 2 + 2 != 4 题解
思路
首先把题干丢进翻译,发现并没有什么作用。
1.1 题目意义的分析
所以可以分析一下 \(2+2=-46\) 的小样例看能不能从中得到什么信息。
利用简单的数学计算出 \(2+2=4\),和我们需要的答案差了 \(50\)。所以可以这样想,在这个题目里 \(+2\) 等同于 \(-48\)。
恰好,\(-48=-50+2\),所以考虑多出来的 \(-50\) 是哪里来的。
转变一下思路可以想到这个 \(-48\) 相当于一个十位为 \(-5\),个位为 \(2\) 的数。
所以考虑这个 \(-5\) 从哪来的。在刚刚的推导中,第二个 \(2\) 已经被用了,所以这个 \(-5\) 应该是从那个加号来的。
很容易想到要把字符对应到数,是 ASCII 码。而加号的 ASCII 码是 \(43\)。突然想到 \(0\) 的 ASCII 码是 \(48\)。
所以题目是把符号当成了数字加入计算,在普通的计算表达式值的过程中,从字符串转到数时要把符号减去 \(48\) 得到的数作为最高位,就像处理普通的数字一样。当然如果前面的符号是加号,就加上我们处理出的数,是减号就减去我们处理出的数。
1.2 代码思路的分析
普通的表达式求值是遇到字符串中的数,就把数变成 \(\texttt{int}\) 加入计算,所以我们可以采取换汤不换药的方法,遇到一个数,先判断前面的符号,将记录数值的变量赋初值为前一个符号的 ASCII 码减掉 \(48\) 的值(而不是 \(0\))。
这是唯一的不同。其他就是正常的表达式求值。
代码
2.1 初始处理
因为我们是遇到数就往前看符号,而第一个数前面并没有符号,所以首先把第一个数取出来。
要用一个变量记录第一个数的长度,因为下一步操作要从第二个数开始。
最后记得把存数值的变量清空。
for(int i=0;i='0'&&s[i]<='9'){
sum*=10;
sum+=s[i]-'0';//非常正常的字符串转数
k=i;//下一步操作要从下一个数开始,所以记录第一个数的长度
}
else{//我们只需要第一个数
break;
}
}
ans=sum;
sum=0;
2.2 处理字符串中单数的值
首先要判断这个位置是一个数字,这个不用多说。
接着要看是什么符号以分开处理。当前面的符号是加号的时候初值就是 \(43-48\);是减号的时候初值就是 \(45-48\)。为了不打成两个if
把中间相同的字符串转数部分写两遍,可以使用三目运算符。因此我们可以把给存数变量赋初值的部分写成:
sum=(s[i-1]=='-'?'-':'+')-'0';
还可以把最后给总答案加减值的部分写成:
s[f-1]=='-'?ans-=sum:ans+=sum;
(因为我们要在字符串转数的时候改变原本的 \(i\),所以用 \(f\) 存这个数开始的位置)
中间则是一个字符串转数的模板:
for(int j=i;j='0'&&s[j]<='9'){
sum*=10;
sum+=s[j]-'0';
i=j;//来一个数字往后面动一位,直到没有数字的时候,i 将会指向这个数后面的符号的前一个位置,而外层循环还有一个 i++。
}
else{//我们只要数,不要符号
break;
}
}
AC 记录
代码不放了,防止抄题解,而且文章里面都给的很明白。
AC 记录