曾经沧海难为水,除却巫山不是云。|

Joey-Wang

园龄:4年3个月粉丝:17关注:0

5.6 大整数运算

5.6 大整数运算

http://codeup.hustoj.com/contest.php?cid=100000593

A a+b 🌟

image-20200719181350693

题目解析

⚠️ 这里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负
大致解题思路:
  1. 使用char数组sa、sb接受输入的数据

  2. 判断sa、sb是否有负号sa[0]==' - ',若有负号则对应flaga=false / flagb=false,且指向sa、sb的指针pa++ / pb++

    【💡让指针++后能很方便的通过change(pa)/change(pb)将原本含负号的char数组转换为绝对值的bign,而不用重新将char数组赋值给另一个数组】🐂🍺

  3. 使用change函数将之倒序转换为bign(大整数的结构体)

  4. 编写cmp函数,用于判断大数a、b之间的大小

  5. 根据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 浮点数加法 🌟🌟

image-20200719183009053

题目解析

  1. 构造结构体bign,用于存储带浮点数的非负数。(题目规定小数部分一定存在)

  2. 用char数组接收输入的数据sa、sb,使用change函数转换为bign;注意要逆序,整数数组的高位对应数据的高位。

    • 例如4543.5435+43.25,我们转换为bign相当于a=3454.5345,b=34.52
  3. 编写add函数实现含浮点数的大数加法

    1. 💡浮点数相加:因为例如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到整数部分。

    2. 整数相加

      • 标准大整数加法,不再复述。
  4. 编写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 进制转换 🌟🌟🌟

image-20200719184632664

题目解析

最初的想法是将m进制转换为10进制,在由10进制转换为n进制,但是这样太麻烦,baidu后发现可以直接做😭。

  1. 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进制

image-20200719190226038

题目解析

这道题其实就是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 中国大陆许可协议进行许可。

posted @   Joey-Wang  阅读(68)  评论(0编辑  收藏  举报
点击右上角即可分享
微信分享提示
评论
收藏
关注
推荐
深色
回顶
展开