zoukankan      html  css  js  c++  java
  • [HDU1002] A + B Problem II

    Problem Description


    I have a very simple problem for you. Given two integers A and B, your job is to calculate the Sum of A + B.


    Input


    The first line of the input contains an integer T(1<=T<=20) which means the number of test cases. Then T lines follow, each line consists of two positive integers, A and B. Notice that the integers are very large, that means you should not process them by using 32-bit integer. You may assume the length of each integer will not exceed 1000.


    Output


    For each test case, you should output two lines. The first line is "Case #:", # means the number of the test case. The second line is the an equation "A + B = Sum", Sum means the result of A + B. Note there are some spaces int the equation. Output a blank line between two test cases.


    Sample Input


    2
    1 2
    112233445566778899 998877665544332211


    Sample Output


    Case 1:
    1 + 2 = 3

    Case 2:
    112233445566778899 + 998877665544332211 = 1111111111111111110

    分析

    说这是个very simple problem,其实是在坑你的~用计算A+B的普通方法是不行的,必须要高精度算法。

    高精度算法就不细说了,大致就是模拟手算。先将个位相加,然后将十位相加,然后百位……当然如果有进位也要算上。要存储A、B两个大数,显而易见,需要使用数组。数组的每个元素都对应一个数位。

    因为A、B最大长度是1000,所以用两个长度为1000的int数组(因为每个元素都是0~9,所以其实char就够了,甚至还可以用一个元素存N位):

    int a[1000], b[1000];

    A、B最大值是999...999(1000个9),因此A+B的最大值是1999...998(999个9),是1001位数。如果用数组c来存储结果,则c长度最大为1001:

    int c[1001];

    数组a、b、c的最后一个元素对应个位,倒数第二个元素对应十位,倒数第三个对应百位……设A、B长度为la、lb,记C=A+B,则C的长度lc最大为max{la, lb} + 1。因此需要从个位一直计算到10^(max{la, lb} + 1)位。最后需要考虑进位。代码:

    void add(const int a[], int la, const int b[], int lb, int c[])
    {
        int lc = ((la > lb) ? la : lb) + 1;
        int g = 0;    // 是否进位
        for (int i = 999; i >= 1000 - lc; i--)
        {
            c[i + 1] = a[i] + b[i] + g;    // 因为c的大小为1001,a和b为1000,所以需要加上1001-1000=1,这样才是从c的末尾开始计算
            g = c[i + 1] / 10;    // 记录进位
            c[i + 1] %= 10;    // 将一个位限定在10以内
        }
    }

    读入的时候就很简单了,如果是C用scanf()加%s,C++直接用cin。C代码:

    char sa[1000], sb[1000];
    scanf("%s%s", sa, sb);

    A、B长度就用strlen()求出。

    输出A和B时,直接将相应字符串输出就好了。至于C呢,可以从c[0]开始一直往后找到第一个非0位然后开始输出,或者记录下C的长度。

    Final Code

    Final code #1

    #include <stdio.h>
    #include <string.h>
    
    #define LEN 1000
    #define LENC 1001
    
    void convert(const char s[], int len, int a[]);
    void add(const int a[], int la, const int b[], int lb, int c[]);
    void print(const int c[]);
    
    int main()
    {
        int t;
        char sa[LEN + 1], sb[LEN + 1];
        int la, lb;
        int a[LEN], b[LEN], c[LENC];
    
        scanf("%d", &t);
    
        for (int i = 0; i < t; i++)
        {
            for (int j = 0; j < LEN + 1; j++)
            {
                sa[j] = '';
                sb[j] = '';
            }
            for (int j = 0; j < LEN; j++)
            {
                a[j] = 0;
                b[j] = 0;
            }
            for (int j = 0; j < LENC; j++)
                c[j] = 0;
            scanf("%s%s", sa, sb);
            la = strlen(sa);
            lb = strlen(sb);
            printf("Case %d:
    %s + %s = ", i + 1, sa, sb);
            convert(sa, la, a);
            convert(sb, lb, b);
            add(a, la, b, lb, c);
            print(c);
            printf("
    ");
            if (i < t - 1)
                printf("
    ");
        }
    
        return 0;
    }
    
    void convert(const char s[], int len, int a[])
    {
        for (int i = 0; i < len; i++)
            a[LEN - len + i] = s[i] - '0';
    }
    
    void add(const int a[], int la, const int b[], int lb, int c[])
    {
        int lc = ((la > lb) ? la : lb) + 1;
        int g = 0;
        for (int i = LEN - 1; i >= LEN - lc; i--)
        {
            c[i + LENC - LEN] = a[i] + b[i] + g;
            g = c[i + LENC - LEN] / 10;
            c[i + LENC - LEN] %= 10;
        }
    }
    
    void print(const int c[])
    {
        int s;
        for (int i = 0; i < LENC; i++)
            if (c[i] != 0)
            {
                s = i;
                break;
            }
        for (int i = s; i < LENC; i++)
            printf("%d", c[i]);
    }

    因为提交时是C语言,所以常量是用#define定义的(const会有编译错误)。而且为了可读性以及逻辑上的清晰,我不惜将运行效率降低了。例如下面代码:

    for (int j = 0; j < LEN + 1; j++)
    {
        sa[j] = '';
        sb[j] = '';
    }
    for (int j = 0; j < LEN; j++)
    {
        a[j] = 0;
        b[j] = 0;
    }
    for (int j = 0; j < LENC; j++)
        c[j] = 0;

    本来可以连起来写成:

    for (int j = 0; j < LEN + 1; j++)
    {
        sa[j] = '';
        sb[j] = '';
        if (j < LEN)
        {
            a[j] = 0;
            b[j] = 0;
        }
        c[j] = 0;
    }

    甚至:

    memset(sa, '', sizeof(sa));
    memset(sb, '', sizeof(sb));
    memset(a, 0, sizeof(a));
    memset(b, 0, sizeof(b));
    memset(c, 0, sizeof(c));

    但为了强调a、b和sa、sb是分开的,就分成了两个循环。为了强调LEN和LENC是无关的(而不是差1的关系),就将a、b和c的初始化分开。而memset()是直接将数组的每个字节设为0,这是与内存有关的,也不能强调将每个元素都设为0(况且这对浮点数组不起作用)。

    当然这份代码也有可以优化的地方,例如查找第一个非0位可以用二分法:

    int sl = 0;
    int sr = LENC - 1;
    while (sl + 1 < sr)
    {
        int sm = (sl + sr) / 2;
        if (c[sm] != 0)
            sr = sm;    // sm为非0位时,说明第一个非0位在sm或sm前面
        else
            sl = sm;    // sm不为非0位时,说明第一个非0位在sm后面
    }

    Final code #2

    这份代码直接在计算A+B时算出C的长度,打印时不需要检查第一个非0位了。

    #include <stdio.h>
    #include <string.h>
    
    #define LEN 1000
    #define LENC 1001
    
    void convert(const char s[], int len, int a[]);
    void add(const int a[], int la, const int b[], int lb, int c[], int * plc); // 多了plc参数
    void print(const int c[], int lc);    // 多了lc参数
    
    int main()
    {
        int t;
        char sa[LEN + 1], sb[LEN + 1];
        int la, lb, lc;    // 声明了lc用来表示C的长度
        int a[LEN], b[LEN], c[LENC];
    
        scanf("%d", &t);
    
        for (int i = 0; i < t; i++)
        {
            for (int j = 0; j < LEN + 1; j++)
            {
                sa[j] = '';
                sb[j] = '';
            }
            for (int j = 0; j < LEN; j++)
            {
                a[j] = 0;
                b[j] = 0;
            }
            for (int j = 0; j < LENC; j++)
                c[j] = 0;
            scanf("%s%s", sa, sb);
            la = strlen(sa);
            lb = strlen(sb);
            printf("Case %d:
    %s + %s = ", i + 1, sa, sb);
            convert(sa, la, a);
            convert(sb, lb, b);
            add(a, la, b, lb, c, &lc);
            print(c, lc);
            printf("
    ");
            if (i < t - 1)
                printf("
    ");
        }
    
        return 0;
    }
    
    void convert(const char s[], int len, int a[])
    {
        for (int i = 0; i < len; i++)
            a[LEN - len + i] = s[i] - '0';
    }
    
    void add(const int a[], int la, const int b[], int lb, int c[], int * plc)
    {
        int lc = (la > lb) ? la : lb;
        int g = 0;
        for (int i = LEN - 1; i >= LEN - lc - 1; i--)
        {
            c[i + LENC - LEN] = a[i] + b[i] + g;
            g = c[i + LENC - LEN] / 10;
            c[i + LENC - LEN] %= 10;
        }
        // 保存C的长度
        *plc = lc;
        // 如果max{la, lb} + 1位非0,则长度+1
        if (c[LENC - lc - 1] != 0)
            (*plc)++;
    }
    
    void print(const int c[], int lc)
    {
        // 直接从LENC - lc处开始打印
        for (int i = LENC - lc; i < LENC; i++)
            printf("%d", c[i]);
    }
  • 相关阅读:
    MySQL常用函数大全讲解
    mysql 获取最近一个月每一天
    Mysql查询某个月的每一天的数据
    Mysql 查询一天中,每个小时数据的数量
    oracle 和 mysql 遍历当前月份每一天
    sql查询总结
    Qt样式表——选择器详解(父子关系,插图详细解释)
    Qt样式表之盒子模型(以QSS来讲解,而不是CSS)
    程序员晋升必备技能——单元测试框架(小豆君的干货铺)
    为什么川普反对中国补贴农业(渐进式发展是非常正确的,如果贸然改动农村土地制度,在城市还不能提供足够的就业岗位下将大量的农民推向城市……请欣赏一下巴西、印度城市的贫民窟)
  • 原文地址:https://www.cnblogs.com/collectionne/p/6786481.html
Copyright © 2011-2022 走看看