A + B(Ⅰ)| 感受算法的魅力

2. A+B (I)

成绩10开启时间2020年09月7日 星期一 09:00
折扣0.8折扣时间2020年09月15日 星期二 09:00
允许迟交关闭时间2020年10月10日 星期六 23:00

Description

学过《计算机科学导论》的你应该熟悉“二进制运算”吧?

和十进制不同的是:二进制运算“逢二进一”。下面举一个二进制加法的运算实例:

   11101
+    110
--------
  100011

下面请你模拟这个过程。

Input

第一行输入一个正整数 T 表示接下来有 T 组数据。
接下来 T 行,分别是两个待相加的二进制串a、b,长度小于10e5。

Output

对于每组数据,请按模拟二进制加法,按题目描述的格式输出正确的运算结果,注意换行,没有多余的空格和换行。


       这个题就是简单的二进制加法实现,主要是几个方式方法要用对,不然处理起来你会觉得很麻烦。以下算法很简洁易懂,都是一些常规操作(如果你第一次见,那你就给我点赞!

1、处理输入

        为了方便输入输出,我们将输入的两个二进制串用字符串a、b储存,最终的结果也用字符串ans储存。

        或许你在思考的时候会遇到很多问题:比如要从最低位加起,我怎么找到最低位(最低位在字符串的最后一个)开始?最后如果有进位怎么办,该如何预先确定最后结果的位数?计算的时候怎么保证每一位对齐相加?

        好了,或许你可以试试这样的思路,让你的困惑烟消云散:我们把字符串a、b倒序储存(这让我们就可以从第1位直接开始算了,也不用考虑对齐的问题),然后处理相加得到结果,最后再将结果翻转回来。

比如题中的用例:

  1. 11101 + 110,那么我们储存字符串 a = "11101"、b = "110"
  2. 将字符串a、b翻转:a = "10111"、b = "011"
  3. 计算ans = a + b = "110001"
  4. 将结果翻转:ans = "100011"就是真实的结果

        至于字符串如何翻转(倒序储存),你可以手动实现,或者直接用c++的一个库函数。reverse()函数在 algorithm 库中,在命名空间std 内(我说这个主要是让你注意在代码最开始加上库和命名空间)。对于一个数组 a 翻转其下标 [start , end) 部分,应该调用函数 reverse(a + start,  a+ end)。(如果你的c语言基础很好的话,你会发现,其实参数就是在传入首尾两个地址,不清白也没关系,先用着吧,你以后就会豁然开朗的)


2、相加的实现

这个算法主要是和计算机里的 “全加器” (好奇可以百度)原理一样,不过你自己也很好想到。

我们肯定是一位位加的,对于每一位的相加我们需要计算:相加的结果位(储存到对应的ans)、进位carry。这里主要是进位carry的使用和更新,我们需要把进位carry带入下一位的计算之中,第一位计算之前carry应初始化为0。比如我们看看这个具体的例子:

下面还有一些细节注意处理以下:

🍳首先我们要知道,两个二进制串的长度不一定一样。所以当某一个字符串的位已经加完而另一个没加完,那我们应该默认已经加完的那些位为0一直相加,知道两个串的每一位都处理完。比如:处理11101 + 110 的时候,我们应该是11101 + 00110这样实现的。

🍳然后我们要注意到,我们为了方便输入输出数字都是用字符串储存的,但是我们在计算的时候要将字符转换为数字计算,计算的结果需要再转回字符储存入ans。


3、处理输出

输出的话把规律找好就行了,主要注意在字符串数组的末尾加上"\0",不然printf函数无法识别出你输出的结束点,很有可能会输出一部分乱码...

我们先要找到整个输出的宽度 = 最后结果串长度 + 2,然后根据这宽度依次推算出每行要补齐多少空格来输出就好了。

我为了代码简洁写了两个输出的函数:

/* 输出num个空格 */
void printBlank(int num) {
    for(int i = 0; i < num; i++)
        printf(" ");
}

/* 输出num个'-',加上换行 */
void printLine(int num) {
    for(int i = 0; i < num; i++)
        printf("-");
    printf("\n");
}


代码实现

还有一个要注意哈,我们定义a、b、ans数组都是在主函数外,也就是定义成全局变量。因为主函数内的栈内存比全局内存要少,如果一些大的内存申请定义在主函数内很容易爆栈,也就是爆内存。你提交上去会出现tle(其实本质应该是mle)。

#include <cstdio>
#include <cstring>
#include <algorithm>

#define LEN 100005
using namespace std;   //声明c++程序的命名空间

/* a、b、ans分别储存输入的两个加数和最后的结果
 * 这里用的是字符串数组,为了方便输入输出
 * 但是在加法计算的时候要将字符转化为数字 */
char a[LEN], b[LEN], ans[LEN];

/* 输出num个空格 */
void printBlank(int num) {
    for(int i = 0; i < num; i++)
        printf(" ");
}

/* 输出num个'-',加上换行 */
void printLine(int num) {
    for(int i = 0; i < num; i++)
        printf("-");
    printf("\n");
}

int main() {
    int T;
    /* 循环处理T次输入 */
    for (scanf("%d", &T); T; T--) {

        /* 处理输入的过程 */
        scanf("%s %s", a, b);

        int len1 = strlen(a);
        int len2 = strlen(b);

        //为了方便计算,我们反转数组,即将我们的个位放到最首、然后十位、百位...
        reverse(a, a + len1);
        reverse(b, b + len2);

        /* 计算过程 */

        int bit = 0;   //从个位(下标为 0)开始计算
        int carry = 0;   //记录进位,初始为 0

        while (bit < len1 && bit < len2) {
            int sum = a[bit] + b[bit] + carry - 2 * '0';  //计算两位之和,记住加上进位(将字符转化为数字!)
            ans[bit] = sum % 2 + '0';  //计算该位的结果(将数字转化为字符)
            carry = sum / 2;  //计算新的进位
            bit++;
        }

        //或许第一个数更长,没加完
        while (bit < len1) {
            int sum = a[bit] + carry - '0';  //计算两位之和,此时b该位数始终为0
            ans[bit] = sum % 2 + '0';  //计算该位的结果(记住加上进位)
            carry = sum / 2;  //计算新的进位
            bit++;
        }

        //或许第二个数更长,没加完
        while (bit < len2) {
            int sum = b[bit] + carry - '0';  //计算两位之和,此时b该位数始终为0
            ans[bit] = sum % 2 + '0';  //计算该位的结果(记住加上进位)
            carry = sum / 2;  //计算新的进位
            bit++;
        }
        //还留有进位
        if (carry != 0) {
            ans[bit++] = carry + '0';
        }
        ans[bit] = '\0'; //现在bit即为结果ans的位数

        /* 输出的过程 */

        //将结果反转回来
        reverse(a, a + len1);
        reverse(b, b + len2);
        reverse(ans, ans + bit);

        int len = bit + 2;  //最终输出的总宽度

        //输出第一个数
        printBlank(len - len1);
        printf("%s\n", a);
        //输出第二个数(最前面有加号)
        printf("+");
        printBlank(len - len2 - 1);
        printf("%s\n", b);
        //输出横线
        printLine(len);
        //输出结果
        printBlank(2);
        printf("%s\n", ans);
    }
}


欢迎关注个人公众号 鸡翅编程 ”,这里是认真且乖巧的码农一枚。

---- 做最乖巧的博客er,做最扎实的程序员 ----

旨在用心写好每一篇文章,平常会把笔记汇总成推送更新~

在这里插入图片描述

 

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值