5.6 大整数运算
5.6 大整数运算
http://codeup.hustoj.com/contest.php?cid=100000593
A a+b 🌟

题目解析
⚠️ 这里a与b不一定都是正数,而且位数在1000以下,所以需要大数运算,需要写高精度加法与高精度减法。【PS 加法减法中的参数都为正数】
- a正、b正 —— add(a, b)
- a正、b负
- a >= |b| sub(a, b)
- a < |b| 输出负号,sub(b, a)
- a负、b正
- b >= |a| sub(b, a)
- b < |a| 输出负号,sub(a, b)
- a负、b负
大致解题思路:
-
使用char数组sa、sb接受输入的数据
-
判断sa、sb是否有负号sa[0]==' - ',若有负号则对应flaga=false / flagb=false,且指向sa、sb的指针pa++ / pb++
【💡让指针++后能很方便的通过change(pa)/change(pb)将原本含负号的char数组转换为绝对值的bign,而不用重新将char数组赋值给另一个数组】🐂🍺
-
使用change函数将之倒序转换为bign(大整数的结构体)
-
编写cmp函数,用于判断大数a、b之间的大小
-
根据flaga、flagb及cmp结果,判断是否输出负号,以及该使用add还是sub函数,最后调用display函数输出结果
代码
#include <cstdio>
#include <cstring>
struct bign {
int d[1005];
int len;
bign() {
memset(d, 0, sizeof(d));
len = 0;
}
};
bign change(char *s) {
bign a;
a.len = strlen(s);
for (int i = 0; i < a.len; i++) {
a.d[i] = s[a.len - 1 - i] - '0';
}
return a;
}
// 若a>=b return true;
bool cmp(bign a, bign b) {
if (a.len > b.len) return true;
else if (a.len < b.len) return false;
else {
for (int i = a.len - 1; i >= 0; i--) {
if (a.d[i] > b.d[i]) return true;
else if (a.d[i] < b.d[i]) return false;
}
return true;
}
}
bign add(bign a, bign b) {
bign c;
int carry = 0;//进位
for (int i = 0; i < a.len || i < b.len; i++) {
int temp = a.d[i] + b.d[i] + carry;
c.d[c.len++] = temp % 10;
carry = temp / 10;
}
if (carry) {
c.d[c.len++] = carry;
}
return c;
}
//前提a>b
bign sub(bign a, bign b) {
bign c;
for (int i = 0; i < a.len || i < b.len; i++) {
if (a.d[i] < b.d[i]) {
a.d[i] += 10;
a.d[i+1] -= 1;
}
c.d[c.len++] = a.d[i] - b.d[i];
}
while (c.d[c.len - 1] == 0 && c.len - 1 >= 1) {
c.len--;
}
return c;
}
void display(bign a) {
for (int i = a.len - 1; i >= 0; i--) {
printf("%d", a.d[i]);
}
printf("\n");
}
int main() {
char sa[1005], sb[1005];
bign a, b;
while (scanf("%s %s", sa, sb) != EOF) {
bool flaga = true, flagb = true;
char *pa = sa, *pb = sb;
if (sa[0] == '-') {
flaga = false;
pa++;
}
if (sb[0] == '-') {
flagb = false;
pb++;
}
//此处a、b是绝对值
a = change(pa);
b = change(pb);
if (flaga && flagb) display(add(a, b));
else if (flaga) {
if (cmp(a, b)) {
display(sub(a, b)); //a正,b负,a >= |b|
} else {
printf("-");
display(sub(b, a)); //a正,b负,a < |b|
}
} else if (flagb) {
if (cmp(b, a)) {
display(sub(b, a)); //a负,b正,b >= |a|
} else {
printf("-");
display(sub(a, b)); //a负,b正,b < |a|
}
} else {
printf("-");
display(add(a, b));
}
}
return 0;
}
C 浮点数加法 🌟🌟

题目解析
-
构造结构体bign,用于存储带浮点数的非负数。(题目规定小数部分一定存在)
-
用char数组接收输入的数据sa、sb,使用change函数转换为bign;注意要逆序,整数数组的高位对应数据的高位。
- 例如4543.5435+43.25,我们转换为bign相当于a=3454.5345,b=34.52
-
编写add函数实现含浮点数的大数加法
-
💡浮点数相加:因为例如4543.5435+43.25,浮点数相加则5435中的35不变,54与43相加。所以我们要保留浮点数部分过长的数据,然后对相同长度的部分进行加法。如 a的小数部分q数组为5345,b的小数部分q数组为52,则53照抄,接下来使用加法处理45与52。
-
所以为了方便👆操作,我们让a的小数部分lenq一定长于b的小数部分lenq,否则就交换二者
-
由👆的思想可得,前a.lenq - b.lenq部分,c.q[c.lenq++] = a.q[i];
后面的部分temp = a.q[i] + b.q[i - a.lenq + b.lenq] + carry; c.p[c.lenp++] = temp % 10; carry = temp / 10;
-
因为加法,最多增加一位,则若carry最大会为1,则表示浮点数部分要加1到整数部分。
-
-
整数相加
- 标准大整数加法,不再复述。
-
-
编写display函数输出bign
- 注意先逆序输出整数部分,再逆序输出小数部分
代码
#include <cstdio>
#include <cstring>
struct bign {
int p[105]; //整数部分
int q[105]; //小数部分
int lenp; //整数部分长度
int lenq; //小数部分长度
bign() {
memset(p, 0, sizeof(p));
memset(q, 0, sizeof(q));
lenp = 0;
lenq = 0;
}
};
bign change(char *s) {
bign a;
bool flag = true;
int len = strlen(s);
for (int i = 0; i < len; i++) {
if (s[len - 1 - i] != '.') {
if (flag) a.q[a.lenq++] = s[len - 1 - i] - '0'; //小数部分
else a.p[a.lenp++] = s[len - 1 - i] - '0'; //整数部分
} else flag = false;
}
return a;
}
//一定为a的小数部分>=b的小数部分
bign add(bign a, bign b) {
bign c;
int carry = 0;
if (a.lenq < b.lenq) {
bign temp = a;
a = b;
b = temp;
}
//a的小数部分比b多,则一开始的数位都为a,后来为a+b
for (int i = 0; i < a.lenq; i++) {
if (i < a.lenq - b.lenq) c.q[c.lenq++] = a.q[i];
else {
int temp = a.q[i] + b.q[i - a.lenq + b.lenq] + carry;
c.q[c.lenq++] = temp % 10;
carry = temp / 10;
}
}
//若小数部分相加后多一位,则carry=1,只需要在整数部分加上就ok
//整数部分的加法
for (int i = 0; i < a.lenp || i < b.lenp; i++) {
int temp = a.p[i] + b.p[i] + carry;
c.p[c.lenp++] = temp % 10;
carry = temp / 10;
}
if (carry) {
c.p[c.lenp++] = carry;
}
return c;
}
void display(bign a) {
for (int i = a.lenp - 1; i >= 0; i--) {
printf("%d", a.p[i]);
}
printf(".");
int pos = 0;
for (int i = 0; i < a.lenq; i++) {
if (a.q[i] == 0) pos++;
else break;
}
for (int i = a.lenq - 1; i >=pos; i--) {
printf("%d", a.q[i]);
}
printf("\n");
}
int main() {
int n;
char sa[105], sb[105];
bign a, b;
while (scanf("%d", &n) != EOF) {
while (n--) {
scanf("%s%s", sa, sb);
a = change(sa);
b = change(sb);
display(add(a, b));
}
}
return 0;
}
D 进制转换 🌟🌟🌟

题目解析
最初的想法是将m进制转换为10进制,在由10进制转换为n进制,但是这样太麻烦,baidu后发现可以直接做😭。
-
m进制转换为n进制,采用除商倒取余的方法。
-
区别在于除商的时候,因为原来bign a都默认10进制,所以某步的余数r=r*10+a.d[i];但是这里bign a为m进制,所以需要r=r*m+a.d[i]
-
搞定如何除商之后,就是不断循环a除商的过程,记录下每一步的余数,直到a=0
【此时a.len=1且a.d[0]=0,故循环条件是while (a.len != 1 || a.d[0] != 0) 】
-
代码
#include <cstdio>
#include <cstring>
struct bign {
int d[10005];
int len;
bign() {
memset(d, 0, sizeof(d));
len = 0;
}
};
bign change(char *s) {
bign a;
a.len = strlen(s);
for (int i = 0; i < a.len; i++) {
if (s[a.len - 1 - i] >= 'A') a.d[i] = s[a.len - 1 - i] - 'A' + 10;
else a.d[i] = s[a.len - 1 - i] - '0';
}
return a;
}
//m进制转化为n进制 的除商步骤 (则只需要每次的余数*m,最后除n)
bign div(bign a, int m, int n, int &r) {
bign c;
c.len = a.len;
r = 0;
for (int i = a.len - 1; i >= 0; i--) {
r = r * m + a.d[i];
if (r < n) c.d[i] = 0;
else {
c.d[i] = r / n; //⚠️别跟加减法一样写成r % n了!!!!
r %= n;
}
}
while (c.d[c.len - 1] == 0 && c.len - 1 >= 1) {
c.len--;
}
return c;
}
bign m_to_n(bign a, int m, int n) {
bign r;
while (a.len != 1 || a.d[0] != 0) {
int temp = 0;
a = div(a, m, n, temp);
r.d[r.len++] = temp;
}
return r;
}
//倒取余刚好和逆序输出相同,所以display函数其实没有变,只是10-要变为a-
void display(bign r) {
for (int i = r.len - 1; i >= 0; i--) {
if (r.d[i] >= 10) printf("%c", 'a' + r.d[i] - 10);
else printf("%d", r.d[i]);
}
printf("\n");
}
int main() {
int m, n;
char s[10005];
bign a, temp;
while (scanf("%d%d", &m, &n) != EOF) {
scanf("%s", s);
a = change(s);
display(m_to_n(a, m, n));
}
return 0;
}
F 10进制 VS 2进制

题目解析
这道题其实就是D进制转换的扩展,那道题做了,这道题看看就会了。再写一个reverse函数掉个顺序就行。
代码
#include <cstdio>
#include <cstring>
struct bign {
int d[1005];
int len;
bign() {
memset(d, 0, sizeof(d));
len = 0;
}
};
bign change(char *s) {
bign a;
a.len = strlen(s);
for (int i = a.len - 1; i >= 0; i--) {
a.d[i] = s[a.len - 1 - i]-'0';
}
return a;
}
void display(bign a) {
for (int i = a.len - 1; i >= 0; i--) {
printf("%d", a.d[i]);
}
printf("\n");
}
//m进制转换为n进制
bign div(bign a, int m, int n, int &r) {
bign c;
c.len = a.len;
r = 0;
for (int i = a.len - 1; i >= 0; i--) {
r = r * m + a.d[i];
if (r < n) c.d[i] = 0;
else {
c.d[i] = r / n;
r %= n;
}
}
while (c.d[c.len - 1] == 0 && c.len - 1 >= 1) {
c.len--;
}
return c;
}
bign m_to_n(bign a, int m, int n) {
bign r;
while (a.len != 1 || a.d[0] != 0) {
int temp;
a = div(a, m, n, temp);
r.d[r.len++] = temp;
}
return r;
}
bign reverse(bign a) {
bign b;
for (int i = a.len - 1; i >= 0; i--) {
b.d[b.len++] = a.d[i];
}
return b;
}
int main() {
char s[1005];
bign a;
while (scanf("%s", s) != EOF) {
a = change(s);
a = m_to_n(a, 10, 2);
a = reverse(a);
a = m_to_n(a, 2, 10);
display(a);
}
return 0;
}
本文作者:Joey-Wang
本文链接:https://www.cnblogs.com/joey-wang/p/14541172.html
版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步