高精度加减乘除各类题目

太久没有写这类题型,都快忘的差不多了,总结一下。

高精度

高精度,也叫大整数计算,是指处理大数字的数学计算方法。在一般的科学计算中,经常会遇到需要计算小数点后几百位甚至更多的数字,或者是几千亿几百亿、几千几万位的大数字。这类数字无法用基本类型进行存储,因此需要使用高精度算法。

高精度算法是用计算机对于超大数据的一种模拟加、减、乘、除、乘方、阶乘、开方等运算。对于非常庞大的数字无法在计算机中正常存储,于是,将这个数字拆开,拆成一位一位的,或者是四位四位的存储到一个数组中,用一个数组去表示一个数字,这样这个数字就被称为是高精度数。高精度算法就是能处理高精度数各种运算的算法。

加法(A+B Problem(高精))

题目描述

高精度加法,相当于 a+b problem,不用考虑负数

输入格式

分两行输入。a,b≤10500。

输出格式

输出只有一行,代表 a+b 的值。

输入输出样例

输入 #1复制

1
1

输出 #1复制

2

输入 #2复制

1001
9099

输出 #2复制

10100

说明/提示

20%20% 的测试数据,00≤a,b≤109;

40%40% 的测试数据,0≤a,b≤1018。

 代码加注释:

#include<stdio.h> // 引入标准输入输出库  
#include<string.h> // 引入字符串处理库  
#include<algorithm> // 引入算法库,用于使用max函数  
using namespace std; // 使用标准命名空间  
  
int main()  
{  
    char s1[100], s2[100]; // 定义两个字符数组,用于存储输入的字符串  
    int a[100], b[100], c[100]; // 定义三个整数数组,用于存储转换后的数字以及相加的结果  
  
    scanf("%s", s1); // 从标准输入读取字符串s1  
    scanf("%s", s2); // 从标准输入读取字符串s2  
  
    memset(a, 0, sizeof(a)); // 将数组a初始化为0  
    memset(b, 0, sizeof(b)); // 将数组b初始化为0  
    memset(c, 0, sizeof(c)); // 将数组c初始化为0  
  
    int len1 = strlen(s1); // 获取字符串s1的长度  
    int len2 = strlen(s2); // 获取字符串s2的长度  
  
    // 将字符串s1逆序存入数组a中  
    for (int i = 0, j = len1 - 1; i < len1; i++, j--) {  
        a[i] = s1[j] - '0'; // 字符转换为数字,'0'的ASCII码为48,故用'0'而不是48  
    }  
  
    // 将字符串s2逆序存入数组b中  
    for (int i = 0, j = len2 - 1; i < len2; i++, j--) {  
        b[i] = s2[j] - '0'; // 同上,字符转换为数字  
    }  
  
    int len = max(len1, len2); // 取两个字符串长度的最大值作为相加后数组c的长度  
  
    // 相加操作  
    for (int i = 0; i < len; i++) {  
        c[i] += a[i] + b[i]; // 每一位相加  
        c[i + 1] += c[i] / 10; // 进位处理  
        c[i] %= 10; // 取个位数  
    }  
  
    int i = 99; // 从数组c的末尾开始  
    while (i > 0 && c[i] == 0) { // 去除前导0  
        i--;  
    }  
  
    // 打印结果  
    for (int j = i; j >= 0; j--) {  
        printf("%d", c[j]);  
    }  
  
    return 0; // 程序正常退出  
}

 减法(高精度减法)

题目描述

高精度减法。

输入格式

两个整数 a,b(第二个可能比第一个大)。

输出格式

结果(是负数要输出负号)。

输入输出样例

输入 #1复制

2
1

输出 #1复制

1

说明/提示

  • 20% 数据 a,b 在 long long 范围内;
  • 100% 数据 0<a,b≤1010086。

代码加注释:

#include<stdio.h> // 引入标准输入输出库  
#include<string.h> // 引入字符串处理库  
#include<iostream> // 引入C++标准输入输出库(这里用不到,可以去掉)  
using namespace std; // 使用标准命名空间(因为包含了iostream,这里用到了)  
  
int main()  
{  
    char s1[100000], s2[100000]; // 定义两个字符数组,用于存储输入的字符串  
    int a[100000], b[100000], c[100000]; // 定义三个整数数组,用于存储转换后的数字以及相减的结果  
  
    scanf("%s", s1); // 从标准输入读取字符串s1  
    scanf("%s", s2); // 从标准输入读取字符串s2  
  
    memset(a, 0, sizeof(a)); // 将数组a初始化为0  
    memset(b, 0, sizeof(b)); // 将数组b初始化为0  
    memset(c, 0, sizeof(c)); // 将数组c初始化为0  
  
    int len1 = strlen(s1); // 获取字符串s1的长度  
    int len2 = strlen(s2); // 获取字符串s2的长度  
  
    // 将字符串s1逆序存入数组a中  
    for (int i = 0, j = len1 - 1; i < len1; i++, j--) {  
        a[i] = s1[j] - '0'; // 字符转换为数字,使用'0'的ASCII码值进行转换  
    }  
  
    // 将字符串s2逆序存入数组b中  
    for (int i = 0, j = len2 - 1; i < len2; i++, j--) {  
        b[i] = s2[j] - '0'; // 同上  
    }  
  
    int f = 0; // 用于标记哪个数更大,以便决定是相减还是相加(这里实际上是相减)  
    if (len2 > len1) f = 1; // 如果s2更长,则s2更大  
    else if (len2 == len1) {  
        for (int i = 0; i < len1; i++) {  
            if (s2[i] > s1[i]) {  
                f = 1;  
                break;  
            }  
        }  
    }  
  
    if (f == 1) { // 如果s2更大  
        for (int i = 0; i < len2; i++) {  
            c[i] += b[i] - a[i]; // 每一位相减  
            if (c[i] < 0) { // 如果结果为负数,则需要向高位借位  
                c[i] += 10; // 加上10,变成正数  
                b[i + 1]--; // 高位减1,表示借位  
            }  
        }  
  
        // 去除前导0并打印负数结果  
        int i = 99999;  
        while (i > 0 && c[i] == 0) {  
            i--;  
        }  
        printf("-"); // 打印负号  
        for (int j = i; j >= 0; j--) {  
            printf("%d", c[j]);  
        }  
    }  
    else { // 如果s1更大或两者相等(但此时f为0,说明s1更大)  
        for (int i = 0; i < len1; i++) {  
            c[i] += a[i] - b[i]; // 每一位相减  
            if (c[i] < 0) { // 如果结果为负数,则需要向高位借位  
                c[i] += 10; // 加上10,变成正数  
                a[i + 1]--; // 高位减1,表示借位  
            }  
        }  
  
        // 去除前导0并打印结果  
        int i = 99999;  
        while (i > 0 && c[i] == 0) {  
            i--;  
        }  
        for (int j = i; j >= 0; j--) {  
            printf("%d", c[j]);  
        }  
    }  
  
    return 0; // 程序正常退出  
}

 乘法(【模板】高精度乘法 | A*B Problem 升级版)

题目背景

本题数据已加强,请使用 FFT 或分治乘法。

题目描述

给你两个正整数 a,b,求 a×b。

输入格式

第一行一个正整数,表示 a;
第二行一个正整数,表示 b。

输出格式

输出一行一个整数表示答案。

输入输出样例

输入 #1复制

83517934
327830610

输出 #1复制

27379735249159740

说明/提示

【数据范围】
1≤a,b≤101000000

可能需要一定程度的常数优化。
数据由 NaCly_Fish 重造

用FFT来解决问题我还没有学会暂时是写不动了。这里简单介绍一下概念。下面的代码主要是高精度乘法模板,没有使用FFT算法。

FFT介绍: 

FFT,即快速傅里叶变换(Fast Fourier Transform),是一种高效的计算离散傅里叶变换(DFT)和其逆变换的算法。DFT是将信号从时间域变换到频率域的一种数学工具,广泛应用于信号处理、图像处理、数值分析和物理学等领域。然而,直接计算DFT的复杂度是O(n^2),对于大数据量来说效率很低。FFT通过利用系数W的周期性和对称性,将DFT中一部分重复的计算合并起来,使得算法复杂度降低到O(n log n),从而大大提高了计算效率。

FFT的基本思想是分治策略。它将一个较长的序列(通常是2的整数次幂个数据)的DFT分解成两个较短的序列的DFT,然后再将这两个较短序列的DFT合并成一个较长的序列的DFT。这个过程可以递归进行,直到分解到最短的序列(通常是长度为1或2的序列)为止。

FFT有多种实现方式,如库利-图基(Cooley-Tukey)FFT算法和基2 DIT-FFT算法等。这些算法在具体实现上有所不同,但基本思想都是利用分治策略来降低计算复杂度

分治乘法介绍: 

分治乘法是一种利用分治策略来解决大整数乘法问题的算法。分治策略的基本思想是将一个规模为N的问题分解为K个规模较小的子问题,这些子问题相互独立且与原问题性质相同。求出子问题的解,然后合并这些解,就可以得到原问题的解。

在大整数乘法中,分治策略可以通过将大整数拆分为更小的整数部分,然后分别计算这些部分的乘积,最后再将结果合并,从而显著减少计算难度和步骤。这种策略可以有效降低大整数乘法所需的计算资源,并提高运算效率。

分治乘法的实现方式有多种,例如对称拆分策略、抽象模型等。此外,还可以结合其他算法技术,如加减运算技术、多位乘运算技术、快速傅里叶变换等,来进一步提升分治乘法的计算速度。

总的来说,分治乘法是一种高效解决大整数乘法问题的算法策略,它通过分解问题、分别求解子问题并合并结果的方式,显著降低了计算复杂度和资源消耗

 常数优化介绍:

常数优化是编程中一种常见的优化手段,它主要是通过改进算法或代码实现,使得算法的运行时间减少,但其时间复杂度不变。这种优化可以在不改变算法基本结构的情况下,显著提高程序的性能

常数优化的方法多种多样,包括但不限于以下几种:

  1. 读入优化:对于输入输出的优化,比如使用快读(fast read)代替标准的输入方式,可以显著提高程序的读入速度。
  2. 寄存器优化:在循环中使用register关键字声明变量,这可以使得变量存储在寄存器中,而非内存中,从而加快访问速度。
  3. 内联函数优化:使用inline关键字将短小的函数定义为内联函数,可以减少函数调用的开销。
  4. 判断语句优化:三目运算符通常比if-else语句更快,因此可以适当地使用三目运算符来优化判断语句。
  5. 赋值优化:对于多组询问的题目,如果数据是静态的,那么无需在每次询问前都进行清空操作。此外,当需要赋值的数组较大且元素值较为集中时,使用memset通常比使用for循环进行赋值更加高效。
  6. 类型优化:选择合适的数据类型可以影响运算的速度。例如,long long类型下的运算通常比int类型慢一些,因此在不需要大整数运算的情况下,应优先使用int类型。
  7. 运算优化:使用位运算来替代一些复杂的算术运算,可以显著提高运算速度。特别是与2的次幂有关的操作,使用位运算通常更加高效。

此外,还有一些更为精致的常数优化方法,例如通过精妙地控制循环的边界或者搜索剪枝来减少不必要的计算,以及及时退出循环、简化布尔表达式、减少变量声明和拷贝等良好的编程习惯。

需要注意的是,虽然常数优化可以提高程序的性能,但过度优化可能会导致代码的可读性和可维护性下降。因此,在进行常数优化时,需要权衡性能提升和代码质量之间的关系。

 这串代码是普通高精度题目的解法思路:

//试过了,这道题通过简单的高精度算不出来,会时间超限。
//下列代码是简单的普通解法,过不了。
#include<stdio.h>  
#include<string.h>  
  
// 定义两个字符串s1和s2来存储输入的大整数  
char s1[1005], s2[1005];  
// 定义三个整型数组a, b, c来存储处理过程中的中间结果  
int a[1005], b[1005], c[1005];  
  
int main()  
{  
    // 初始化数组a, b, c为0  
    memset(a, 0, sizeof(a));  
    memset(b, 0, sizeof(b));  
    memset(c, 0, sizeof(c));  
  
    // 读取输入的两个大整数  
    scanf("%s%s", s1, s2);  
  
    // 获取两个大整数的长度  
    int len1 = strlen(s1);  
    int len2 = strlen(s2);  
  
    // 将s1逆序存储到数组a中,转换为整型数组  
    for (int i = 0, j = len1 - 1; i < len1; i++, j--) {  
        a[i] = s1[j] - '0';  
    }  
  
    // 将s2逆序存储到数组b中,转换为整型数组  
    for (int i = 0, j = len2 - 1; i < len2; i++, j--) {  
        b[i] = s2[j] - '0';  
    }  
  
    // 执行大整数乘法运算  
    for (int i = 0; i < len1; i++) {  
        for (int j = 0; j < len2; j++) {  
            // 计算乘积,并累加到数组c的对应位置  
            c[i + j] += a[i] * b[j];  
            // 处理进位  
            c[i + j + 1] += c[i + j] / 10;  
            c[i + j] %= 10;  
        }  
    }  
  
    // 找到数组c中的最高位非零元素的位置  
    int i = 1004;  
    while (i > 0 && c[i] == 0) {  
        i--;  
    }  
  
    // 输出最终结果  
    for (int j = i; j >= 0; j--) {  
        printf("%d", c[j]);  
    }  
  
    return 0;  
}

除法(A/B Problem)

 题目描述

输入两个整数 a,b,输出它们的商。

输入格式

两行,第一行是被除数,第二行是除数。

输出格式

一行,商的整数部分。

输入输出样例

输入 #1复制

10
2

输出 #1复制

5

说明/提示

0≤a≤105000,1≤b≤109。

代码加解析:

#include<stdio.h>  
#include<string.h>  
  
// 定义全局变量  
int b = 0, a[100000], c[1000000];  
char s1[100000];  
  
int main() {  
    // 初始化数组a和c为0  
    memset(a, 0, sizeof(a));  
    memset(c, 0, sizeof(c));  
      
    // 从标准输入读取字符串s1  
    scanf("%s", s1);  
      
    // 从标准输入读取整数b  
    scanf("%d", &b);  
      
    // 计算字符串s1的长度  
    int len = strlen(s1);  
      
    // 将字符串s1中的字符转换为数字并存储到数组a中  
    for (int i = 0; i < len; i++) {  
        a[i] = s1[i] - '0';  
    }  
      
    // 用于存储中间结果的变量  
    long long ans = 0;  
      
    // 计算除法结果并存储到数组c中  
    for (int i = 0; i < len; i++) {  
        c[i] = (ans * 10 + a[i]) / b;  
        ans = (ans * 10 + a[i]) % b;  
    }  
      
    // 查找商数组c中第一个非零元素的索引  
    int i = 0;  
    while (i < len && c[i] == 0) {  
        i++;  
    }  
      
    // 输出商  
    if (i < len) { // 如果找到了非零元素  
        for (int j = i; j < len; j++) { // 从第一个非零元素开始输出,直到商的最后一位  
            printf("%d", c[j]);  
        }  
    } else {  
        // 如果整个商都是零  
        printf("0"); // 输出0  
    }  
      
    return 0;  
}

  • 40
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值