小工具知识网

当前位置: 首页 > 行业知识 > 算法基础1.2:高精度,前缀和,差分

行业知识

算法基础1.2:高精度,前缀和,差分

2026-10-30 21:43:57 来源:互联网转载

高精度运算:

只���C++需要高精度的学习,JAVA和python则不需要,因为JAVA是大整数类,没有位数要求,python默认数的范围就是无限大,只有C++没有大整数类。

高精度主要服务于比较大的整数加减乘除,一般来说高精度考四种。

第一种的话是两个比较大的整数相加(a和b的位数大概是10的六次方)。

第二种是两个比较大的整数相减(a和b的位数大概是10的六次方)。

第三种是一个大整数乘上一个小的整数。(大整数的长度小于10的6次方,小的整数小于等于10的9次方,是特别长的一个数。

第四种是a÷b求商和余数是多少,

比如求方案数,n个苹果,选出来若干个。问有多少种选法,答案是二的N次方,当n是1000的时候,一万的时候,那么它总共的位数肯定是3000位,就很长了。

第一个问题:大整数的存储

大整数,在C++里面代码里面是如何表示的?首先会爆掉int变量,用int变量肯定是存不下来的,大多数时候其实是把它的每一位存到数组里面去,,比方说一个大整数是123456789,一共是九位的一个整数,我们会把它的每一位存到数组里面去,存的时候就有一个问题,我们应该是高位在前呢,还是低位在前呢?假设我们有一个数组的下标是从012345678,第0位应该存一还是存九呢?其实第0位应该存九比较好。数组里边分别存的987654321,为什么把个位存到这个数组的第一个数,然后把最高位存到最后一个数呢?主要是因为两个整数相加,乘法或除法的时候,可能会有进位,就需要在高位上补上一个数,数组的话就在最后一个数,在数组的末尾补上一个数是最容易的,在数组的开头,如果想补上一个数,那就需要把整个数组全部往后平移一位啊,比较麻烦。但是如果往末尾添加一个数字的话,就很好加了,就直接往后push back 1个数就可以了,因此我们存的时候,个位是数组中第零个下标是零的,这个位置存个位,下标是一的,这个位置存十位,以此类推。和我们一般的写法是反着的,我们一般写的话会先写高位,但是在数组中会先写低位。加减乘除的大整数存储都一样。主要是因为题目里面最终答案可能是很大的,中间把答案算出来,这个过程可能会包含加减乘除。必须要这几个运算要同时进行,所以整数存储的格式就一致

高精度1.加法

高精度的加法是一个模拟人工加法的过程,与小学加法相同。用代码去模拟相加进位这个过程,假设已有A和B数组,那么A数组的A0是个位,A1是十位,以此类推到A3,B组比方只有三B2B1B0,B0存的是个位。然后我们A和B相加,首先算a0加上b0是多少,如果大于十的话,就进一位,如果小于十的话,就不进位。然后新的个位就是a+b.模十,比方说写成c0,二位再算的时候就是a1加上b1,再加上进位,算一下是不是大于十,如果大于十的话,就进位,如果不大于十的话,就不进位,新十位就是三个数的和模10,求出c1,以此类推。算每一位的时候,实际上是三个数相加,就是Ai+Bi,再加上上一位的进位,比方叫做t,上一位的进位是t,上一位如果没有进位的话t就是零啊,如果有进位的话t就是一。最开始的也可以让t=0,相当于进了一个零。每一位都是这样,如果Ai或者Bi不存在,那么我们就把它看成零。

模板题:

给定两个正整数(不含前导 0),计算它们的和。

输入格式

共两行,每行包含一个整数。

输出格式

共一行,包含所求的和。

数据范围

1≤整数长度≤100000

输入样例:
1223
输出样例:
35

#include

#include     //vector 是变长数组,支持随机访问,不支持在任意位置 O(1)插入。为了保证效率,元素的增删一般应该在末尾进行。

using namespace std;

vector add(vector &A, vector &B)    //加上引用的目的是为了提高效率,如果不加引用就会把整个数组copy 1遍,如果加上引用就不会拷贝整个数组。

{

    if (A.size()

    vector C;     //C=A+B

    int t = 0;     //定义进位

    for (int i = 0; i

    {

        t += A[i];

        if (i

        C.push_back(t % 10);

        t /= 10;  //t大于10进位,否则不进位

    }

    if (t) C.push_back(t);   //如果最高位还有进位,就在C的最后加上

    return C;

}

int main()

{

    string a, b;     //用字符串读入

    vector A, B;   //vector a; 相当于一个长度动态变化的 int 数组,vector b[233]; 相                                                                 当于第一维长 233,第二位长度动态变化的 int 数组

    cin >> a >> b;    //a='123456'

    for (int i = a.size() - 1; i >= 0; i -- ) A.push_back(a[i] - '0');   //第一步大整数存储,把每一位抠              出来放到vector中去,a.push_back(x) 把元素 x 插入到 vector a 的尾部,A=[6,5,4,3,2,1]

    for (int i = b.size() - 1; i >= 0; i -- ) B.push_back(b[i] - '0');//所有的容器都可以视作一个“前闭后开”的结构,因此逆序输出,size要减一。数组中存储的是字符类型的数字, 要变成真实整数的值需要                                                                          减去一 个字符0

    auto C = add(A, B);     //auto意为让计算机自己判断变量类型,避免过长类型的重复输入

    for (int i = C.size() - 1; i >= 0; i -- ) cout B.size();   //首先比较位数,位数多则大

    for (int i = A.size() - 1; i >= 0; i -- )   //其次从高位开始逐位比较,注意高位为最后一个数

        if (A[i] != B[i])

            return A[i] > B[i];

    return true;   //如果相等,返回ture

}

vector sub(vector &A, vector &B)

{

    vector C;

    for (int i = 0, t = 0; i

    {

        t = A[i] - t;

        if (i

        C.push_back((t + 10) % 10);   //t一定是个位数,A-B大于等于0返回t,小于0则返回t+10,                                                                  (t+10)%10可包 含两种情况

        if (t

        else t = 0;

    }

    while (C.size() > 1 && C.back() == 0) C.pop_back();  //减法有一个问题,比方说123-120,答案是三,a有多少位c就有多少位,所以返回的其实是003,需要把前导零去掉,所以需要一个while循环,只要c的最后一位,首先c的size肯定得大于一。如果它只有一位,是零的话,不能把它减掉,要给它留上一位零,并且最后一位如果等于零的话,就把最后一位去掉。

pop_back()意为删除 vector 的最后一个元素。

    return C;

}

int main()

{

    string a, b;      //这个题并没有考虑很复杂的情况,比方说如果有负数怎么做?如果有负数,不是模板的问题,其实是数输出的问题,需要判断一下a和b这两个字符串,判断第一个字母是不是负号,如果是负号,需要打一个标记。存一下每个数是不是负数,如果两个数相减,有可能是正也有可能是负的,一定可以转化成两数绝对值相减或者绝对值相加的情况,一定可以用两个模板算出来。如果不是正数,需要分情况讨论。

    vector A, B;

    cin >> a >> b;

    for (int i = a.size() - 1; i >= 0; i -- ) A.push_back(a[i] - '0');

    for (int i = b.size() - 1; i >= 0; i -- ) B.push_back(b[i] - '0');

    vector C;

    if (cmp(A, B)) C = sub(A, B);  //判断A是否大于B,大于则A-B,小于则-(B-A)

    else C = sub(B, A), cout = 0; i -- ) cout > a >> b;

    vector A;

    for (int i = a.size() - 1; i >= 0; i -- ) A.push_back(a[i] - '0');

    auto C = mul(A, b);

    for (int i = C.size() - 1; i >= 0; i -- ) printf("%d", C[i]);

    return 0;

}

作者:yxc

链接:https://www.acwing.com/activity/content/code/content/39794/

来源:AcWing

高精度4.除法

人工除法,列数式,比方说1234除上11,首先从第零位开始算,因为计算机不能直接看到第0位不够,所以第零位应该商零,余数是一,把这个余数乘以十。加上下一位数字2,然后下一位是12÷11为一,所以商一余一,把这个余数乘以十,再加上三得13啊,下一位再商一余二,二乘10加4为24,商2余2,最后余数是二。除法也是一个高精度的整数除上一个低精度的整数,假设我们的高精度数是a3a2a1a0除上一个b,每一次就判断一下当前这个余数,这个余数最一开始就只有a3,就直接求a3除b的余数,然后商到这就是最高位c3然后除完之后,余数会变成a3减去一个b×c3,等价于a3除上b的一个余数是多少,余数假设是r1,那么r1乘上十加上下一位a2就会得到a1a2前两位的一个余数,把前两位的一个余数整除b就可以得到c2,同理,再把这个数模上b,得到下一个余数r2,补上下一位r2a1,等于r2乘上十加上a1,因为a的值一直补到余数的后面一位,其实就是r乘10加a1,然后再把这个数除以b求一个商,就是c1,然后再求一下这个数对于b的余数r3,然后最后算一下r3a0这个数除b商是c0,然后最后再算r3a0模b的余数r4

模板题:

给定两个非负整数(不含前导 0) A,B,请你计算 A/B的商和余数。

输入格式

共两行,第一行包含整数 A,第二行包含整数 B。

输出格式

共两行,第一行输出所求的商,第二行输出所求余数。

数据范围

1≤A的长度≤100000,

1≤B≤10000,

B 一定不为 0

输入样例:
72
输出样例:
31

#include

#include

#include

using namespace std;    //除法是从最高位开始算的,加法减法乘法都是从最低位开始算,所以如果题目里面只有除法的话啊,正着存数可能会好一些,但是一般情况下,题目里加减乘除这几种运算是混合的,为了能让这几种运算统一起来,在算的时候没有任何的问题。那么除法存储的时候,也是按照前面的存储方式倒着来存。

vector p(vector &A, int b, int &r)    //余数r通过引用返回

{

    vector C;

    r = 0;   //余数最开始是零

    for (int i = A.size() - 1; i >= 0; i -- )

    {

        r = r * 10 + A[i];

        C.push_back(r / b);

        r %= b;

    }

    reverse(C.begin(), C.end());  //注意第一位c0存的是最高位,c1存的是最低位。跟前面定义大整数的存法是逆过来的,所以这里需要reverse 1遍,加上算法的头文件。

    while (C.size() > 1 && C.back() == 0) C.pop_back();     //去前导0

    return C;

}

int main()

{

    string a;

    vector A;

    int B;

    cin >> a >> B;

    for (int i = a.size() - 1; i >= 0; i -- ) A.push_back(a[i] - '0');

    int r;

    auto C = p(A, B, r);

    for (int i = C.size() - 1; i >= 0; i -- ) cout

上一篇:【Django自学】Django入门:如何使用django开发一个web项目(非常详细)

下一篇:轻量服务器怎么备案?