题目链接

https://www.dotcpp.com/oj/problem1583.html

题目大意

求a,b的乘积,a,b不超过10000位。

解题思路

高精度,毋庸置疑。

高精度乘法(高精度*高精度)

本质思想:
字符串输入 -> 逆置 -> 加减乘除 -> 判断最终长度 -> 逆向输出
大数通过输入字符串的方式输入,再把字符串转化成int数组,int数组每个位置放置的是大数的每一位。对int数组进行操作。
核心代码:

        cin>>s1+1>>s2+1;//以字符串形式输入
    len1=strlen(s1+1);//求出位数
    len2=strlen(s2+1);
    for(int i=1;i<=len1;i++)
        a[len1-i+1]=s1[i]-'0';//将大数逆置,不明白可以看下面的解释
    for(int i=1;i<=len2;i++)
        b[len2-i+1]=s2[i]-'0';//逆置

    for(int i=1;i<=len1;i++)    
        for(int j=1;j<=len2;j++){
            c[i+j-1]+=a[i]*b[j];//非常巧妙,i+j-1正好是i和j相乘之后直接影响的位置。注意是+=
            c[i+j]+=c[i+j-1]/10;//先让i+j-1的高一位加上进位。注意是+=
            c[i+j-1]%=10;//计算本位的数
        }

逆置:
图片说明
为什么要逆置?
让低位在小索引位置,方便进位。你想想要是不逆置,直接每一位每一位操作,当操作到两数的最高位(即索引为0 或者 1)时,若发生进位,最高位索引为1还好说,要是为0,进位如何保存?而逆置之后,向高索引位置进位就十分容易了。
因此,我们可以写出代码了:

#include<bits/stdc++.h>
using namespace std;
const int N=10010;
const int M=2*N;
int c[M],a[N],b[N];
int len1,len2;
char s1[N],s2[N];
int main(){
    scanf("%s%s",s1+1,s2+1);
    //cin>>s1+1>>s2+1;
    len1=strlen(s1+1);
    len2=strlen(s2+1);

    for(int i=1;i<=len1;i++)
        a[len1-i+1]=s1[i]-'0';
    for(int i=1;i<=len2;i++)
        b[len2-i+1]=s2[i]-'0';

    for(int i=1;i<=len1;i++)    
        for(int j=1;j<=len2;j++){
            c[i+j-1]+=a[i]*b[j];
            c[i+j]+=c[i+j-1]/10;
            c[i+j-1]%=10;
        }

    int len=len1+len2-1;//也是非常巧妙的地方,记住就ok,而且也不难理解,自己比划比划就知道了
    if(c[len+1]!=0) len++;//判断一下,要是还有进位保留在len+1的位置,说明len+1位置也有数,就让len++

    for(int i=len;i>=1;i--)//因为高索引是高位,输出的时候要先输出高位,因此先输出高索引。
        printf("%d",c[i]);//cout<<c[i];
}

哎,你会发现并没法AC,这是因为数据规模的问题,当相乘的数都为1w位时,复杂度为1e8,不大行!
所以,引入进阶版高精度 乘以 高精度
压位操作:
我们上面的a数组每一个元素是一位数,这样挺浪费的,所以,我们这次每一个元素存4位数。
图片说明
a数组的长度变短了,所以循环的次数也就少了,时间复杂度也就低了。
压位核心代码:(这里得自己理解一下了)

    for(int i=len1;i>=1;i--,tmp*=10)//tmp是10的某次幂
        if((len1-i)%4==0) a[++alen]=s1[i]-'0',tmp=1;
        else a[alen]=(s1[i]-'0')*tmp+a[alen];

    for(int i=len2;i>=1;i--,tmp*=10)
        if((len2-i)%4==0) b[++blen]=s2[i]-'0',tmp=1;
                else b[blen]=(s2[i]-'0')*tmp+b[blen];

AC代码

#include<bits/stdc++.h>
using namespace std;
const int N=10010;
const int M=2*N;
int c[M],a[N],b[N];
char s1[N],s2[N];
int alen,blen,tmp;
int main(){
    cin>>s1+1>>s2+1;
    int len1=strlen(s1+1);
    int len2=strlen(s2+1);

    for(int i=len1;i>=1;i--,tmp*=10)
        if((len1-i)%4==0) a[++alen]=s1[i]-'0',tmp=1;
        else a[alen]=(s1[i]-'0')*tmp+a[alen];

    for(int i=len2;i>=1;i--,tmp*=10)
        if((len2-i)%4==0) b[++blen]=s2[i]-'0',tmp=1;
        else b[blen]=(s2[i]-'0')*tmp+b[blen];

    for(int i=1;i<=alen;i++)
        for(int j=1;j<=blen;j++){
            c[i+j-1]+=a[i]*b[j];
            c[i+j]+=c[i+j-1]/10000;//除10000,不是除1000!!!debug好久
            c[i+j-1]%=10000;
        }

    int len=alen+blen-1;
    if(c[len+1]!=0) len++;

    for(int i=1;i<=len;i++)
        if(i!=1) printf("%04d",c[len-i+1]);//重点!当遍历到非最高四位时,要保证输出4位,不足4位前面补零
        else cout<<c[len];//最高四位,直接输出,不带0
} 

特别注意

特别注意,当存在乘数为0的情况时,需要特判并输出0!!!!
我在这debug过好久!

压位一般压到四位,因为1e4 * 1e4 < 2147483647,但是开五位就超int了;
先手算一下压到四位时间复杂度会不会超,若还是超,可以压到七位,但是要开LL。

吐槽

网上搜高精度乘法压位,搜到的都是些什么东西啊,讲的不忍直视,要不就代码奇葩,可读性差(应该是我太菜了),所以才写了这篇。