ARTICLE DETAIL

资讯详情

深耕编程入门与网站建设的一线实战洞察。

高精度问题

高精度问题 高精度问题一、基本了解C 中普通整数能存多大的数int大约 ±9e9long long大约 ±9e18看着很大对不对但是竞赛、算法题里经常出现这种数据输入一个 100 位、200 位、1000 位的超大整数求加法、乘法比如12345678901234567890...100位这种数任何原生整型都存不下直接爆掉。解决办法高精度算法本质用字符串 / 数组 手动模拟小学生竖式计算。高精度就是自己手写加减乘除规则处理超长整数核心思想数字太长存不下需要拆成一位一位存进数组计算途中有两位数时就要模仿竖式手动进位我们人脑读数高位在前比如12341是千位最高位但是高精度代码统一规则数组低位存数字低位反转存储示例数字1234数组存成a[0]4, a[1]3, a[2]2, a[3]1为什么反转因为加减乘都是从个位开始算、往高位进位低位放前面下标刚好对齐二、高精度加法原理照搬小学竖式从个位逐位相加保留个位剩下的进位给下一位模板#includebits/stdc.h using namespace std; const int N1e5; // 设置数组最大容量能够存储很长的大数 int a[N],b[N],res[N]; // a[]、b[]存放两个输入大数res[]存放相加结果存储规则低位在前 int main(){ string s1,s2; cins1s2; // 用字符串读取超大整数超过long long范围不能直接用数字变量存储 int las1.size(); // 获取第一个数字字符串的长度 int lbs2.size(); // 获取第二个数字字符串的长度 // 将字符串转为低位在前的整型数组 // 举例字符串1234 → a[0]4a[1]3a[2]2a[3]1 for(int i0;ila;i){ a[i]s1[la-1-i]-0; } for(int i0;ilb;i){ b[i]s2[lb-1-i]-0; } int t0; // t用来保存加法进位初始进位为0 // 模拟竖式加法循环执行到较长数字的最高位 for(int i0;imax(la,lb);i){ t t a[i] b[i]; // 当前位总和 上一轮进位 a当前数位 b当前数位 res[i] t % 10; // 取个位作为结果当前位 t t / 10; // 十位部分作为新的进位参与下一位运算 } // 处理输出结果数组低位在前需要倒序打印 if(t!0){ // 循环结束仍有进位说明多出最高一位 res[max(la,lb)]t; // 从新增最高位倒序遍历输出 for(int imax(la,lb);i0;i--){ coutres[i]; } } else{ // 没有剩余进位从最长数的末尾向前输出 for(int imax(la,lb)-1;i0;i--){ coutres[i]; } } return 0; }三、高精度减法原理竖式减法不够减向前借位前提我们默认s1 s2模板#includebits/stdc.h using namespace std; const int N1e5; int a[N],b[N],res[N]; int main(){ string s1,s2; cins1s2; int las1.size(); int lbs2.size(); //字符串转【低位在前】数组 for(int i0;ila;i){ a[i]s1[la-1-i]-0; } for(int i0;ilb;i){ b[i]s2[lb-1-i]-0; } int t0; //t代表借位初始0 //逐位相减 for(int i0;ila;i){ // 当前位 a本位 - b本位 - 上一轮借位 int now a[i] - b[i] - t; t 0; //清空本次借位 if(now 0){ //不够减需要向前借1 now 10; t 1; //标记下一位需要减1 } res[i] now; } // 去除前导零例如 1000-9991不要输出0001 int len la; while(len 1 res[len-1]0){ len--; } //逆序输出 for(int ilen-1;i0;i--){ coutres[i]; } return 0; }四、高精度乘法原理小学竖式每一位乘每一位错位相加公式res[ij] a[i] * b[j]模板#includebits/stdc.h using namespace std; const int N1e5; int a[N],b[N],res[N]; int main(){ string s1,s2; cins1s2; int las1.size(); int lbs2.size(); // 字符串转【低位在前】数组 for(int i0;ila;i){ a[i] s1[la-1-i] - 0; } for(int i0;ilb;i){ b[i] s2[lb-1-i] - 0; } // 核心乘法a第i位 × b第j位累加至 res[ij] for(int i0;ila;i){ for(int j0;jlb;j){ res[ij] a[i] * b[j]; } } // 统一处理进位 int t0; // 两个数相乘最多 lalb 位 for(int i0;ilalb;i){ t res[i]; res[i] t % 10; t / 10; } // 去除前导零 int len la lb; while(len1 res[len-1]0){ len--; } // 逆序输出 for(int ilen-1;i0;i--){ coutres[i]; } return 0; }位置规律a[i]代表第 (10^i) 位b[j]代表 (10^j) 位(10^i *10^j 10^{ij})所以乘积存到res[ij]和加法区别加法一层循环逐位运算乘法两层循环枚举所有数位两两相乘先累加、最后统一进位五、核心知识点总结高精度解决的问题超出 long long 范围的超大整数运算存储方式字符串读入 → 反转存入数组低位在前加法核心逐位相加、记录进位减法核心不够减向前借位、去前导零乘法核心i,j 错位累积、统一处理进位数组开双倍空间防止数组越界输出方式逆序输出数组六、什么时候用高精度数字位数 ≥ 20 位大数阶乘、大数幂运算超大数加减乘答案数值极大无法用 long long 存储七、例题洛谷P1045麦森数[P1045NOIP 2003 普及组] 麦森数 - 洛谷#includebits/stdc.h using namespace std; int n,a[1000]{0},res[1000]{0}; void mul1(){ int temp[1000]{0}; for(int i0;i500;i){ for(int j0;j500;j){ temp[ji]temp[ji]res[i]*a[j]; } } int t0; for(int i0;i500;i){ temp[i]t; res[i]temp[i]%10; ttemp[i]/10; } } void mul2(){ int temp[1000]{0}; for(int i0;i500;i){ for(int j0;j500;j){ temp[ij]a[i]*a[j]; } } int t0; for(int i0;i500;i){ temp[i]t; a[i]temp[i]%10; ttemp[i]/10; } } void quick_pow(int p){ res[0]1,a[0]2; while(p){ if(p1){ mul1(); } mul2(); p1; } } int main(){ cinn; int ln*log10(2)1; coutlendl; quick_pow(n); res[0]-1; int c0; for(int i499;i0;i--){ if(c50){ coutendl; c0; } coutres[i]; c; } return 0; }L-A × B_河南萌新联赛2026第四场南阳理工学院#includebits/stdc.h using namespace std; #define int long long signed main(){ string a,b; cinab; reverse(a.begin(),a.end()); reverse(b.begin(),b.end()); vectorintres(a.size()b.size()); for(int i0;ia.size();i){ for(int j0;jb.size();j){ int a1a[i]-0; int b1b[j]-0; res[ij]res[ij]a1*b1; } } int c0; for(int i0;ires.size();i){ int sumres[i]c; res[i]sum%10; csum/10; } string ans; bool oktrue; for(int ires.size()-1;i0;i--){ if(res[i]0ok){ continue; } okfalse; ans.push_back(res[i]0); } coutans; return 0; }
返回列表