《算法竞赛从入门到国奖》算法基础:入门篇-高精度

💡Yupureki:个人主页

✨个人专栏:《C++》 《算法》


🌸Yupureki🌸的简介:


目录

前言

[1. 高精度加法](#1. 高精度加法)

算法原理

实操代码

[2. 高精度减法](#2. 高精度减法)

算法原理

实操代码

[3. 高精度乘法](#3. 高精度乘法)

算法原理

实操代码

[4. 高精度除法](#4. 高精度除法)

算法原理

实操代码


前言

当数据范围极其之大,甚至超过了long long的存储范围时,我们需要利用字符串来进行加减乘除的运算。因此我们需要先利用字符串存储数据,随后模拟数学中加减乘除的过程。这一算法称之为大数运算,也就是高精度运算

1. 高精度加法

题目链接:

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

算法原理

我们会发现数据范围为10的18次方,对于这么大的数字,我们利用整型来存储肯定是不合适的,因此得用字符串,也就是string存储,然后进行加法

那么如何进行字符串之间的加法?我们回想小学学过的竖式乘法

首先便是对应位相加,例如上面6 + 3得9,2+8得10,注意此时相加的10已经超过9了,我们得处理进位,因此留下0,在1+4中需要加上进位1得6,最后得到609。

此时有个小细节,即相加是从最低位开始的,即个位相加后再十位相加

那么对于字符串"123"和"486"相加,我们也得从最后面,即3和6相加开始,但是从后往前加有点不爽,这里我选择把两个字符串倒置,得到"321"和"684",这样从每个字符串开头相加就可以了,得到字符串"906",此时结果字符串仍是倒置的,我们再次倒置得到"609"即可

实操代码

cpp 复制代码
#include <iostream>
#include <string>
#include <algorithm>
using namespace std;

int main()
{
    string a, b; cin >> a >> b;
    reverse(a.begin(), a.end());//倒置两个字符串
    reverse(b.begin(), b.end());
    int forward = 0;//初始时进位设置为0
    int left = 0;
    string ret;
    int i = 0;
    while (i < a.size() || i < b.size())
    {
        int n = 0; int m = 0;
        if (i < a.size())
            n = a[i] - '0';
        if (i < b.size())
            m = b[i] - '0';
        left = (n + m + forward) % 10;//处理余数
        ret += left + '0';
        forward = (n + m + forward) / 10;;//处理进位
        i++;
    }
    if (forward)//别忘了处理最后的进位
        ret += forward + '0';
    reverse(ret.begin(), ret.end());
    cout << ret;
    return 0;
}

2. 高精度减法

题目链接:

P2142 高精度减法 - 洛谷

算法原理

跟小学数学一样,我们模拟竖式减法即可

但过程中如果第一个数小于第二个数,可能会出现减法。这里为了方便,我们始终让大数减去小数得到正数,如果第一个数较小,最后多加个负号即可

依然是从个位开始,对应位相减,如果不够,那么从高位借一即可

实操代码

cpp 复制代码
#include <iostream>
#include <string>
#include <algorithm>
using namespace std;

int main()
{
    string a, b; cin >> a >> b;
    string ret;
    if (b.size() > a.size() || (a.size() == b.size() && a < b))
    {
        cout << "-";
        swap(a, b);//保持较大数减较小数
    }
    reverse(a.begin(), a.end());
    reverse(b.begin(), b.end());
    int i = 0;
    while (i < a.size() || i < b.size())
    {
        int n = 0; int m = 0;
        if (i < a.size())
            n = a[i] - '0';
        if (i < b.size())
            m = b[i] - '0';
        int left = n - m;
        if (left < 0)
        {
            left += 10;//向高位借1
            a[i + 1]--;
        }
        ret += left + '0';
        i++;
    }
    reverse(ret.begin(), ret.end());
    auto it = ret.begin();
    while (it != ret.end() && *it == '0')
        it++;
    if (it == ret.end())
        cout << "0";
    else
    {
        while (it != ret.end())
        {
            cout << *it;
            it++;
        }
    }
    return 0;
}

3. 高精度乘法

题目链接:

P1303 A*B Problem - 洛谷

算法原理

依然是模拟小学的竖式相乘,但较麻烦的是,乘法很容易出现二位数,因此需要频繁地进行进位处理,这里为了方便,我们不进位,到最后相乘完再进位

例如在这里,我们不进行进位操作,对于每一位我们直接相加得到最后的结果,最后再进位

当然也有个小细节,相乘的结果估计会很大,我们需要预设一个较大的数组来存储,但是这样也有可能导致数组有部分空间是空出来的,最后得到的字符串前面会有很多无效0,例如0000887112,因此我们需要在最后去除这些前导0

实操代码

cpp 复制代码
#include <iostream>
#include <string>
#include <vector>
#include <algorithm>
using namespace std;

int main()
{
    string a, b; cin >> a >> b;
    vector<int> v(a.size() + b.size(), 0);
    reverse(a.begin(), a.end());
    reverse(b.begin(), b.end());
    for (int i = 0; i < a.size(); i++)
    {
        for (int j = 0; j < b.size(); j++)
        {
            int num = (a[i] - '0') * (b[j] - '0');
            v[i + j] += num;//不处理进位,直接相加
        }
    }
    int forward = 0;
    int left = 0;
    for (int i = 0; i < v.size(); i++)
    {
        left = (forward + v[i]) % 10;
        forward = (forward + v[i]) / 10;
        v[i] = left;
    }
    reverse(v.begin(), v.end());
    auto it = v.begin();
    while (it != v.end() && *it == 0)
        it++;
    if (it == v.end())
        cout << "0";
    else
    {
        while (it != v.end())
        {
            cout << *it;
            it++;
        }
    }
    return 0;
}

4. 高精度除法

题目链接:

P1480 A/B Problem - 洛谷

算法原理

我们依然模拟小学学过的除法,但是这次我们是从最高位开始除,因此我们无需倒置字符串

最后得到的字符串前面也有0,因此我们需要处理这些前导0

实操代码

cpp 复制代码
#include <string>
#include <algorithm>
using namespace std;

int main()
{
    string a;int b;cin>>a>>b;
    string ret;
    long long left = 0;
    for(int i = 0;i<a.size();i++)
    {
        long long num = a[i] - '0' + left*10;
        ret += (num/b) +'0';
        left = num % b;
    }
    auto it  = ret.begin();
    while(it != ret.end() && *it == '0')
        it++;
    if(it == ret.end())
        cout<<"0";
    else
    {
        while(it != ret.end())
        {
            cout<<*it;
            it++;
        }
    }
    return 0;
}
相关推荐
不会书1 天前
Linux字符设备驱动的演进:从传统框架到现代实践
linux·运维·服务器·c语言·驱动开发
whm27771 天前
Visual Basic 拖放
开发语言·visual studio
行云流水20001 天前
青少年编程考级覆盖哪些科目?图形化Python C++全包含
开发语言·c++·青少年编程
代码游侠1 天前
应用——Linux 标准IO编程
linux·前端·数据库·学习·算法
小尧嵌入式1 天前
在windows上安装ffmpeg及新版ffmpeg命令
c++·windows·算法·ffmpeg
beordie1 天前
LeetCode 1. 两数之和 | 从暴力到哈希表的优化之路
算法
别动哪条鱼1 天前
AVFrame的data数组数据结构详解
网络·数据结构·ffmpeg
free-elcmacom1 天前
机器学习进阶<5>K-means智能客户分群与可视化分析系统
算法·机器学习·kmeans
csuzhucong1 天前
海盐折纸
算法