一、概述
高精度整数的意思即是数据范围极大,已经无法用unsigned long long存储的数据的运算。很多OJ中都有相关练习题(如a+b升级版等),而对于故意给出极大数据的题目此算法为常用算法。
本章仅研究对于10进制以内(2进制~10进制)数的加法。本章将使用例题的讲解方法。
二、例题
例题1
已知两个10进制数a,b,求a+b的值。
数据范围
对于a,b,50%的数据不大于10100。
输入输出
输入1
201647458466448 116476489143158
输出1
318123947609606
分析
对于这个数据范围,很明显要使用高精度运算方法。
高精度加法的流程如下。以本题示例为例。
(1)使用char[]存储两个大整数,并将其全部存入int[];
(2)用一个新的int[]存储做完加法的数,按位全部加起来;
(3)处理进位。
流程如下图所示。
实现
#include <cstdio>
#include <cstring>
char a1[105], b1[105];
int a[105], b[105], c[105];
int main(void) {
FILE *f = fopen("in.txt", "r");
memset(a, 0, sizeof(a));
memset(b, 0, sizeof(b));
memset(c, 0, sizeof(c));
fscanf(f, "%s%s", a1, b1);
fclose(f);
int alen = strlen(a1), blen = strlen(b1);
int i;
// 倒序放入数字
for(i = 0; i < alen; i++) a[alen - i] = a1[i] - '0';
for(i = 0; i < blen; i++) b[blen - i] = b1[i] - '0';
int clen, decade = 0;
for(clen = 1; clen <= alen || clen <= blen; clen++) {
c[clen] = a[clen] + b[clen] + decade;
decade = c[clen] / 10; // 存储本位的进位
c[clen] %= 10; // 删去本位进位
}
c[clen] = decade; // 处理最高位的进位
if(c[clen] == 0) clen--; // 修正进位后大整数的位数
// 进行完上述过程后,c数组中从0至clen的元素已经存储了各位数字,只需将它们反向输出
for(i = clen; i >= 1; i--) {
printf("%d", c[i]);
}
return 0;
}
// 注:上面关于一些关键角标的+1或-1操作是为了跳过char[]标志结束的\0
例题2
已知两个n进制数a,b,求a+b(n进制)的值。
数据范围
对于a,b,50%的数据不大于10100。
输入输出
输入1
10
201647458466448 116476489143158
输出1
318123947609606
输入2
2
11111111101010100011 11010001101000101011
输出2
111010001010011001110
分析
更改例题1实现中进位处理即可。
实现
#include <cstdio>
#include <cstring>
char a1[105], b1[105];
int a[105], b[105], c[105];
int main(void) {
FILE *f = fopen("in.txt", "r");
memset(a, 0, sizeof(a));
memset(b, 0, sizeof(b));
memset(c, 0, sizeof(c));
int n;
fscanf(f, "%d%s%s", &n, a1, b1);
fclose(f);
int alen = strlen(a1), blen = strlen(b1);
int i;
for(i = 0; i < alen; i++) a[alen - i] = a1[i] - '0';
for(i = 0; i < blen; i++) b[blen - i] = b1[i] - '0';
int clen, decade = 0;
for(clen = 1; clen <= alen || clen <= blen; clen++) {
c[clen] = a[clen] + b[clen] + decade;
decade = c[clen] / n; // 使用n进制
c[clen] %= n;
}
c[clen] = decade;
if(c[clen] == 0) clen--;
for(i = clen; i >= 1; i--) {
printf("%d", c[i]);
}
return 0;
}
例题3
(本题来自NOIP1999提高组复赛试题,可以在Vijos查看此题,本题也是CCF官方教材入门篇P231练习第5题,Vijos提交记录AC)
若一个数(首位不为零)从左向右读与从右向左读都一样,我们就将其称之为回文数。
例如:给定一个10进制数56,将56加56(即把56从右向左读),得到121是一个回文数。
又如:对于10进制数87:
STEP1:87+78 = 165 STEP2:165+561 = 726
STEP3:726+627 = 1353 STEP4:1353+3531 = 4884
在这里的一步是指进行了一次N进制的加法,上例最少用了4步得到回文数4884。
写一个程序,给定一个N(2<=N<=10或N=16)进制数M,其中16进制数字为0-9与A-F,求最少经过几步可以得到回文数。如果在30步以内(包含30步)不可能得到回文数,则输出“Impossible!”
数据范围
2<=N<=10或N=16
0<=M<=maxlongint
输入输出
输入1
9
87
输出1
STEP=6
分析
本题由于进制不确定以及可能造成的数据过大,可以考虑采用例题2的高精度运算处理,可以使算法更加灵活。
注意:要特殊处理16进制。这里采用的是将16进制翻译成10进制,再翻译回16进制的处理方法。本题解不是最优解。(记得注释掉调试输出!)
实现
#include <cstdio>
#include <cstring>
#include <cctype>
const int MAXN = 10001;
int a[MAXN], b[MAXN], c1[MAXN];
const char hex[] = "0123456789ABCDEF";
// 高精度加法,返回结果的位数
int hplus(int n, char a1[], char b1[], char c[]) {
memset(a, 0, sizeof(a));
memset(b, 0, sizeof(b));
memset(c1, 0, sizeof(c1));
int alen = strlen(a1), blen = strlen(b1);
int i;
for(i = 0; i < alen; i++) a[alen - i] = isdigit(a1[i]) ? a1[i] - '0' : a1[i] - 'A' + 10;
for(i = 0; i < blen; i++) b[blen - i] = isdigit(b1[i]) ? b1[i] - '0' : b1[i] - 'A' + 10;
int clen, decade = 0;
for(clen = 1; clen <= alen || clen <= blen; clen++) {
c1[clen] = a[clen] + b[clen] + decade;
decade = c1[clen] / n;
c1[clen] = c1[clen] % n;
//printf("a %d b %d c1 %d\n", a[clen], b[clen], c1[clen]);
}
c1[clen] = decade;
if(c1[clen] == 0) clen--;
int j = 0;
for(i = clen; i >= 1; i--) {
//printf("i %d c1 %d\n", i, c1[i]);
c[j] = hex[c1[i]]; // 16进制
j++;
}
return clen;
}
void rev(char string[], char reved[]) {
int len = strlen(string);
int i;
for(i = 0; i < len; i++) {
reved[len - i - 1] = string[i];
}
}
char input[MAXN], reved[MAXN], temp[MAXN];
int main(void) {
//FILE *f = fopen("in.txt", "r");
int n;
scanf("%d%s", &n, input);
//fclose(f);
int i;
for(i = 0; i <= 30; i++) {
memset(reved, 0, sizeof(reved));
rev(input, reved);
//printf("input %s reved %s\n", input, reved);
if(!strcmp(input, reved)) {
printf("STEP=%d", i);
return 0;
}
hplus(n, input, reved, temp);
strcpy(input, temp);
}
printf("Impossible!");
return 0;
}
三、总结
高精度加法(其实减法也可以用这样的思想去写)其实做了一件模拟手算列竖式的这样一个工作。但需要注意这里对char[]的复杂操作极容易写错,写这个算法的时候记着小心一些。