洛谷:P1601:A+B Problem(高精)


洛谷:P1601:A+B Problem(高精)

题目

P1601:A+B Problem(高精)

分析

本题着重于高精度计算中的加法运算,而这是一个模拟过程。

回想我们进行加法时的列竖式过程,从中可以直观地转换到算法。

高精度类

本题解法中,我用到了C++的类。这是高级语法,不要求掌握。不过其中讨论的基本思路是适用的,稍加改造就可以变成纯数组实现。

先给出类的定义如下:

class HighPrecision
{
    friend istream& operator>>(istream& is, HighPrecision& hp);
    friend ostream& operator<<(ostream& os, const HighPrecision& hp);
    friend HighPrecision operator+(const HighPrecision& hp1,
                                   const HighPrecision& hp2);

   private:
    vector<int> num;

   public:
    HighPrecision() {}
    HighPrecision(const string& s)
    {
        for (int i = s.size() - 1; i >= 0; i--)
            num.push_back(s[i] - '0');
    }
};

首先,我们重载两个运算符(输入和输出)。这个内容不要求掌握。

然后重载+运算,然后是用来存放各位数字的一个私有成员vector<int> num,并声明两个构造函数。请注意第二个构造函数HighPrecision(const string& s)的实现:它是把数字的位从低位到高位依次存放(也就是将字符串的末尾字符先放入 num[0])。之所以这样存放,是为了后续加法时可以从低位开始顺序遍历各个位数,便于处理进位。

实现加法

回顾列竖式加法的过程:各位对齐,从最低位开始两两相加得到一个和。这个和如果大于等于10,那么就会产生进位。我们需要将进位记下来,而当前位只保留这个和的个位数。后续各位相加的时候,要算上“进位”的数值。

用代码实现的时候,我们可以直接将“进位”先加到下一位,这样就不用专门处理进位加法。也就是说,不用a + b + carry,而是直接这么做:

hp.num[i] += a + b;
hp.num[i + 1] = hp.num[i] / 10;  // Carry to next position
hp.num[i] %= 10;                 // Keep only the digit

这样一来,在算下一位的时候,结果的hp.num[i+1]位已经有了进位,可以直接加a + b了。

结果的位数

输入的两个数字的位数不一定相同,而且相加后可能会多出一位(也就是进位的那一位)。不过,我们用的是vector,它的大小是动态调整的,不用刻意预留。如果你用静态数组保存最终结果,就需要考虑这个问题,要将数组的大小设为int result[max_digit+1],其中的max_digit是两个加数中更长的位数。

去掉前导0

题目中没有说输入就一定是规范的,所以可能出现0000000000001 + 0000034454这样的情况,也就是结果数组中有大量的前导0。但我们的输出必须规范。所以要去掉这些前导0(但如果只有一个0,这个0必须保留)。


// Remove leading zero if no final carry
if (hp.num.back() == 0 && hp.num.size() > 1)
{
    hp.num.pop_back();
}

答案

思考

加法的算法还可以更简单一些:

HighPrecision operator+(const HighPrecision& hp1, const HighPrecision& hp2)
{
    HighPrecision hp;
    int carry = 0;
    int maxLen = max(hp1.num.size(), hp2.num.size());

    // 循环条件:当仍有未处理的位或仍有进位时继续
    for (int i = 0; i < maxLen || carry; i++)
    {
        int sum = carry;
        if (i < hp1.num.size()) sum += hp1.num[i];
        if (i < hp2.num.size()) sum += hp2.num[i];

        hp.num.push_back(sum % 10);
        carry = sum / 10;
    }
    return hp;
}

这两种写法是等价的。

Previous Next