写在前面:
本系列博客仅作为本人十一假期过于无聊的产物,对小学期的程序设计作业进行一个总结式的回顾,如果将来有BIT的学弟学妹们在百度搜思路时翻到了这一条博客,也希望它能对你产生一点帮助(当然,依经验来看,每年的题目也会有些许的不同,所以不能保证每一题都覆盖到,还请见谅)。
不过本人由于学艺不精,代码定有许多不足之处,欢迎各位一同来探讨。
同时请未来浏览这条博客的学弟学妹们注意,对于我给出完整代码的这些题,仅作帮助大家理解思路所用(当然,因为懒,所以大部分题我都只给一个伪代码)。Anyway,请勿直接复制黏贴代码,小学期的作业也是要查重的,一旦被查到代码重复会严厉扣分,最好的方法是浏览一遍代码并且掌握相关的要领后自己手打一遍,同时也要做好总结和回顾的工作,这样才能高效地提升自己的代码水平。
加油!
指路类似题:力扣JFETK5
成绩 | 10 | 开启时间 | 2021年08月24日 星期二 09:00 |
折扣 | 0.8 | 折扣时间 | 2021年08月27日 星期五 23:00 |
允许迟交 | 否 | 关闭时间 | 2021年10月10日 星期日 23:00 |
Description
学过《计算机科学导论》的你应该熟悉“二进制运算”吧?
和十进制不同的是:二进制运算“逢二进一”。下面举一个二进制加法的运算实例:
11101 + 110 -------- 100011
下面请你模拟这个过程。
Input
第一行输入一个正整数(
),表示接下来有
组数据,接下来
行,每行输入两个二进制串
和
,中间用空格隔开,保证他们的长度
并且没有前导
。
Output
对于每组数据,请按模拟二进制加法,按题目描述的格式输出正确的运算结果,注意换行,没有多余的空格和换行。
测试用例 1 | 以文本方式显示
| 以文本方式显示
| 1秒 | 64M | 0 |
题意分析:
看到这道题的时候觉得小学期的作业“不过如此”。
没想到这只是暴风雨来临前的宁静。
在大一的程序设计基础课程里应该有过高精度加法(名词解释:高精度)的题,我们先从最朴素的算法想起(忽然想起,最最最朴素的想法应该是直接把二进制转成十进制求和以后再输出二进制,当然,毫无疑问地爆了),这类题最自然的想法应该是“模拟”,即用类似于人做竖式加法的方法来求解,亦即对齐、从右到左依次相加、进位、输出。
- 对齐,不必多说,考虑到字符串的读入在逻辑上都是左对齐,而竖式计算需要右对齐,因此需要有一个对齐的操作(当然,此处有优化空间,稍后会再提到)。
- 相加,也就是相加(废话)。但其实这道题有一个隐式条件,即不能使用“+”操作符。原因是计算机对于“+”操作符的调用,实际上是通过二进制加法来完成的,所以当我们在设计二进制加法算法的时候还没有出现“+”这个操作符,逻辑上来说是不可以使用的。(当然,题目没有这么要求,乐学系统也没那么智能,并不会因为你使用了“+”就扣分)。
- 进位,也就是进位(废话+1)。在十进制加法的竖式计算中,会出现两数相加结果大于10的情况,这时候就要向前进一位,二进制加法也是同理。
如果你只是想要通过乐学系统的判准,到这里已经足够了,不过我相信BIT的学生应该都想要挑战一下更快更强的算法,其实这题也是有不少优化空间的,例如:
- 对齐的操作实际上是一个复杂而低效的操作,我们平时列竖式时对齐只是为了方便计算,实际上不对齐也可以有效地算出最终的结果,所以也没有可能简化对齐这个步骤呢?答案必然是肯定的,在此提供两种思路,一是在读入的时候就逆序地读入,即按下标从小到大存储从低位到高位的数据,这样对齐补“0”的这个过程中避免了移位的操作(实际上,在memset初始化之后就已经全是0了),同时如果最后加和结果产生了进位也不需要额外地进行移位操作,只需要向高位递补,非常地方便,这样的弊端在于虽然省去了补齐的操作,但仍然需要逆序这个操作,且相较于人的加法逻辑而言比较地不自然;二是不进行补齐操作,而在加和处理的时候从各自的串尾开始相加,得到的数由低到高地存入一个新数组中,直到其中一串到达串首,就终止循环,将另一串剩余内容依次输出到数组中,这样的弊端在于需要额外的一个数组的空间。二者孰优孰劣,仁者见仁智者见智。
- 既然不能使用“+”,那要如何处理加法呢?答案是位运算(名词解释:位运算),当然,位运算在算法中是非常高深莫测的玩意儿,此处我们只用到最基础的几个位运算符:按位与运算符“&”,按位异或运算符“^”,左移运算符“<<”,右移运算符“>>”,这几个运算符的具体操作可以参考上面给出的超链接。
话说回来,这些个运算符到底是如何取代加号的地位的呢?首先考虑两个最简单的二进制数“0”和“1”,他们的相加无非四种情况:同为“0”→结果为“0”;有一个“0”有一个“1”→结果为“1”;同为“1”→结果为“10”,即在本位上的结果为0,同时保存了一个“1”的进位。于是我们不难发现,在一位数加法这个过程中涉及到的两个量:和的本位
与进位
我们都可以用位运算符来表示,其中
^
,
&
。而在多位数加法的过程中,每一位的加法都与一位数加法类似,唯一的区别是引入了一个新的量,即上一次的进位
,也只需要多进行一次操作,即将
再与
做一次加法运算,如果产生了进位则保存到
中(若为此情况,则先前
中必然不可能保存进位,想想为什么),如此循环往复,直到结束,我们就得到了最终的结果。
伪代码:
读入组数;
分组循环:
读入两个二进制串(此处可以学会用string类保存二进制串,后续操作会更便捷);
反转两个字符串(可以采用string类的reverse方法);
从低位至高位循环,待计算的位上对应的两个数为a与b:
初始化CIN为0; (这里S和COUT初不初始化都行,不过我建议是统一初始化)
S = a ^ b; COUT = a & b; (此处记得将字符 - ‘0’,将ASCII码转为实际上的数)
S = S ^ CIN; COUT = S & CIN;
将S覆盖到长字符串的低位上,保存CIN进入下一次循环;
输出;(输出时记得看清楚空格的数量和‘-’的数量)