HDU1002_杭电OJ1002(A + B Problem II)_大数加法_C语言版@FDDLC

考点:大数加法。

Accepted的代码:

#include<stdio.h>
#include<string.h>
#define MAX_LENGTH 1005
char a[MAX_LENGTH],b[MAX_LENGTH],s[MAX_LENGTH];

void str_reverse(char *str)
{
    int len=strlen(str);
    for(int i=0; i<len/2; i++)
    {
        int temp=str[i];
        str[i]=str[len-1-i];
        str[len-1-i]=temp;
    }
}

void zero_initialize(char *str, int begin_index, int end_index)
{
    for(int i=begin_index; i<end_index; i++)
        str[i]='0';
}

void sum()
{
    str_reverse(a);
    str_reverse(b);
    zero_initialize(a, strlen(a), MAX_LENGTH);
    zero_initialize(b, strlen(b), MAX_LENGTH);
    int carry=0;
    for(int bit_i=0; bit_i<MAX_LENGTH; bit_i++)
    {
        int bit_sum=(a[bit_i]-'0')+(b[bit_i]-'0')+carry;
        s[bit_i]=(bit_sum)%10+'0';
        carry=bit_sum/10;
    }
    for(int bit_i=MAX_LENGTH-1; s[bit_i]=='0'; bit_i--)
        s[bit_i]='\0';
    if(strlen(s)==0) //结果为0(对应输入: 0 + 0)。但本题其实无需考虑此种情况,因为“each line consists of two positive integers”
        s[0]='0';
    str_reverse(s);
}

int main()
{
    int case_n;
    scanf("%d", &case_n);
    for(int case_i=1; case_i<=case_n; case_i++)
    {
        scanf("%s%s", a, b);
        if(case_i!=1)
            printf("\n");

        printf("Case %d:\n%s + %s = ", case_i, a, b);
        sum();
        printf("%s\n", s);
    }
    return 0;
}

 

失败提交1:Presentation Error

其实和AC的代码并无太大不同,就是一个换行符 '\n' 的区别。(说实话,有时候这种小问题还是挺难发现的)

 

失败提交2Compilation Error

原因:此次提交选择的是GCC编译器,但OJ的GCC版本太老旧了,不支持C99或C11的部分语法。比如:

不支持的示例:

for(int i=0; i<10; i++)

支持的示例:

int i;

for(i=0; i<10; i++)

最快的解决办法:代码不变,换G++提交!

 

失败提交3:Wrong Answer

//other code
char *str_reverse(char *str)
{
    int len=strlen(str);
    for(int i=0; i<len/2; i++)
    {
        int temp=str[i];
        str[i]=str[len-1-i];
        str[len-1-i]=temp;
    }
    return str;
}
//other code
char *sum(char *_a, char *_b)
{
    char *a=str_reverse(_a);
    char *b=str_reverse(_b);
    zero_initialize(a, strlen(a), MAX_LENGTH);
    zero_initialize(b, strlen(b), MAX_LENGTH);
    char s[MAX_LENGTH];
    //other code
}

int main()
{
    char a[MAX_LENGTH], b[MAX_LENGTH];
    int case_n;
    //other code
}

这个错误是最让我抓狂的。为什么呢?因为一开始我自己测试的时候,好像没毛病啊!各种测试数据都能完美通过!

为什么OJ却报错呢?

不过还好,我有一个信念:“Wrong Answer” 一定是测试数据没通过!

于是我想,题目说结果可能达到1000位数字,但我还没测试这种情况,问题可能在这。于是我用代码生成了1000位的测试用例,自己再一运行,果然就出错了!

但我又想,我的代码逻辑怎么看都没问题啊!

不过还好,知道问题后我隐约想起在哪里看过:函数里的数组和栈等资源不要开得太大,因为有内存限制!

于是我把数组放到函数外,声明为全局性的!这次自己再用1000位的大数测试也没问题。于是提交,结果不用说,当然是AC啦!

可惜的是,这样代码就不那么美了,因为函数的独立性没了。(一个好的程序应该做到:高内聚,低耦合。)

 

关于核心函数 sum() 的讲解:

为了讲解方便,假设 MAX_LENGTH=8,而不是1005。

假设输入的 a 为"123",b 为"4567",a、b的内存情况如下:

1、str_reverse()把a、b 这两个字符串,以 '\0' (a以a[3],b以b[4])为界,把 '\0' 之前的字符串反转,而 '\0' 和 '\0' 之后的字符则不变。反转后的结果如下:

 

2、关于void zero_initialize(char *strint begin_indexint end_index):

这个函数的作用是把str[begin_index]……str[end_index-1](注意最后一个不是str[end_index])逐一赋值为字符 '0'。

在执完步骤1后,执行:

    zero_initialize(astrlen(a), MAX_LENGTH);

    zero_initialize(bstrlen(b), MAX_LENGTH);

注意到strlen(a)为3,strlen(b)为4(因为strlen()以为 '\0'就是字符串的终点),并且已经假设过MAX_LENGTH为8,所以上面两句相当于:

    zero_initialize(a, 3, 8); //对a[3]……a[7]赋'0'

    zero_initialize(b, 4, 8); //对b[4]……b[7]赋'0'

执行完后a、b的内存情况如下:

为什么要执行步骤2,或者说为什么要对a、b的后面部分进行赋 '0' 呢?因为这样不管 i 为多少,a[i]+b[i]只有一种情况,而无需分类进行处理。(具体就不展开细讲了,不知读者有没有理解其中奥妙)

 

3、执行a[i]+b[i](a[i]和b[i]都是 '0' 到 '9' 的字符):

    for(int bit_i=0bit_i<MAX_LENGTH; bit_i++)

    {

        int bit_sum=(a[bit_i]-'0')+(b[bit_i]-'0')+carry;

        s[bit_i]=(bit_sum)%10+'0';

        carry=bit_sum/10;

    }

执行完后 s 的内存情况如下:


4、将多余的字符 '0' 换成字符 '\0'。

    for(int bit_i=MAX_LENGTH-1s[bit_i]=='0'bit_i--)

        s[bit_i]='\0';

具体做法就是从后往前,即从s[7]往前执行如下操作:

令bit_i=7,如果s[bit_i]为 '0' ,则把s[bit_i]改为 '\0' ,再 bit_i-- ,当遇到第一个非 '0' 字符时停止操作。

这里s[7]、s[6]、s[5]、s[4]会被置 '\0'。此步执行完后s的内存情况如下:

 

5、对结果字符串s执行反转函数:str_reverse(s);

再次强调一遍,str_reverse()只反转 '\0' 之前的部分。比如这里反转的是s[0]、s[1]、s[2]、s[3],而s[4]、s[5]、s[6]、s[7]不动。执行完此步后s的内存情况如下:

 

6、输出s:printf("%s\n", s);

注意printf()同样是遇到 '\0' 就认为该字符串到头了。所以只输出s[0]、s[1]、s[2]、s[3],即结果为4690。而以值为 '\0' 的s[4]为界,不管后面是 '\0' 这种不可见字符,还是 '8' 这种可见字符,都不会输出。

 

拓展阅读

1、对 0 + 0 这种特殊情况的针对性处理:

    if(strlen(s)==0) //结果为0(对应输入: 0 + 0)。但本题其实无需考虑此种情况,因为“each line consists of two positive integers”

        s[0]='0';

注:上面这个操作可以放在步骤5前面,也可以放在步骤5后面!

当计算 0 + 0 时,上面步骤3得到的结果是:s[0]为 '0' ,s[1]为 '0' ,……,s[MAX_LENGTH-1]也为 '0' 。再执行步骤4得到的结果是:s[0]为 '\0' ,s[1]为 '\0' ,……,s[MAX_LENGTH-1]也为 '\0' ,即 s 是一个空串!空串输出也是空的,但正确结果却应该是0!

那怎么处理呢?

我们知道,空串的长度,即strlen()值为0!所以我们只需特判一下:if(strlen(s)==0),则令s[0]='0'这样就能输出正确结果0了。

 

2、再来回顾一下步骤4:

强调:上面for循环里的 s[0]='\0'至少要执行一次!

为什么呢?

我们知道,上面代码的作用之一是产生字符串结束符 '\0'。如果 s[0]='\0'不执行,意味着字符串结束符 '\0'没有产生,即s[0]到s[MAX_LENGTH-1]都没有 '\0' !依赖 '\0' 来标识一个字符串的结束的步骤5、6,就会接着非法处理后续的s[MAX_LENGTH]、s[MAX_LENGTH+1]、s[MAX_LENGTH+2]、……,直到找到一个 '\0' 为止!也是够执着的!

 

 

本文完:各位同行,如果有帮助的话,就让我听个响呗!

 

 

相关推荐
©️2020 CSDN 皮肤主题: 编程工作室 设计师:CSDN官方博客 返回首页