专题1:高精度算法

暑假开始啦!然而某市四中某s老师给他的学生下达了任务:在暑假结束前,要在某知名评测网站上AC 100题

在s老师的学生中有一位w同学看了看自己的刷题量

愤怒的他开始刷水题,直到他看见了一道喜闻乐见的A+B problem(出自洛谷P1601)

他一看,哇!金色!传说!这种题还用想吗?上代码

结果自然是

经过20分钟+的挣扎,w同学终于放弃了,来找同为蒟蒻但是却会一些高精度的g同学帮忙

他再一看,数据范围(a,b<=10^500)就蒙了

且不说这个同学不看题的毛病

我们来讨论一下,为什么会WA

Q:你觉得电脑存储的数字大小有极限吗?

如果你觉得没有,那你就大错特错了

上面的例子就是一个鲜明的写照

在c++中,如果使用int型存储,其最大限制约为-2^31~2^31-1(约有10位数)

如果使用long long int型存储,其最大限制约为-2^63~2^63-1(约有19位数)

而超过这个大小的数,会输出奇奇怪怪的内容(以下为用上面的代码打出的结果)

而高精度算法,就是来解决这一切的(我来!)

使用高精度,可以存上千万位数的大数!

什么?你说你的一个数要有上亿位?你见过有人把数组开上亿的吗,那都是新手才干的。

你赢了,但是在比赛中,绝对不会出现这样的数(虽然高精度也很难会出)(出这种题的人会被全国OIer骂的)

Part 1 高精度的输入与存储

//part 1 高精度的存储方法
//<1>由于高精度的特殊性,我们采用string字符串格式存储高精度整数
//<2>也可采用循环加数组的方法进行存储,不能直接输入数字,要作为字符输入,之后转换为数字 
//tip<1>:string格式不能使用scanf("%s")输入,只能使用cin输入,之后将其存入数组中,利用数组进行计算 
//tip<2>:string格式的首位是从0开始,但利用length求长度时会比此多一位 
//tip<3>:在方法2中,由于最后一位是空格或回车,故真正的数组长度为length-1 
#include<bits/stdc++.h>
using namespace std;
int a[12000];
char k[12000];
void init1()
{
	string s;
	cin>>s;//tip<1> 
	a[0]=s.length();
	for(int i=1;i<=a[0];++i)
	{
		a[i]=s[i-1]-48;//tip<2> 
	}
	for(int i=1;i<=a[0];++i)
	{
		printf("%d",a[i]);
	}
}
void init2()
{
	int length=0;
	for(int i=1;;++i)
	{
		scanf("%c",&k[i]);
		if(k[i]==' '||k[i]=='\n')
		{
			length=i;//tip<3> 
			break;
		}
	}
	for(int i=1;i<length;++i)
	{
		a[i]=k[i]-48;
	}
	for(int i=1;i<length;++i)
	{
		printf("%d",a[i]);
	}
} 
int main(){
	init1();//<1> 
	init2();//<2>
}

Part 2 高精度的加法

在高精度算法中,大多数都是按照竖式方式解,如果无法理解可以对照代码自己模拟理解

//part 2 高精度加法
//<1>同时存储两个数组,这个代码使用的是上面代码中的方法<1> 
//<2>a数组存储被加数,b数组存储加数,c数组存储结果 
//tip<1>:一定注意s,m,a,b,c数组的起始位置,s及m作为字符串数组,其起始位置为0.a,b,c数组作为存储数的数组,其起始位置为1
//tip<2>:清楚两数相加时的进位过程 
//tip<3>:在两数相加时,我们的位数(lenc)是从1往上增,这是我们采取倒序输出并且处理最高进位的原因 
#include<bits/stdc++.h>
using namespace std;
int a[10000],b[10000],c[10000],lena,lenb,lenc,x;
string s,m;
int main()
{
    cin>>s>>m;
    lena=s.length();
    lenb=m.length();
    for(int i=0;i<=lena-1;++i)a[lena-i]=s[i]-48;
    for(int i=0;i<=lenb-1;++i)b[lenb-i]=m[i]-48;
	lenc=1;x=0;
    while(lenc<=lena||lenc<=lenb)
    {
        c[lenc]=a[lenc]+b[lenc]+x;//x可以看做一个进位标记,在下一位才使用 
        x=c[lenc]/10;//这也是为什么先处理x再++lenc 
        c[lenc]%=10;
        ++lenc;//位数变化 
    }
    c[lenc]=x;
    if(c[lenc]==0)--lenc;//处理最高进位 
    for(int i=lenc;i>=1;--i)printf("%d",c[i]);//倒序输出 
    return 0;
}

看到这里,w同学松了一口气,看来他终于可以把那道坑人的A+B problem 给AC掉了,喜加一。

可他没想到,还有更多的题向他涌来

乐于助人的g同学决定帮人帮到底,接下来,我们帮他把这一个专题过完!

Part 3 高精度减法

//part 3 高精度减法 
//<1>和加法的思路相似,但需要注意的是,被减数必须比减数大 
//<2>处理进位和处理借位有所不同,但还是很相似 
//tip<1>:当被减数小于减数时,高精度算法不再适用,所以要交换被减数和减数,并输出负号 
//tip<2>:string字符串并没有有效的比较大小方式(至少g同学不知道),所以转换为字符数组进行判断 
//tip<3>:在循环减数的过程中,由于不知道lena与lenb哪个大,所以只要lenc满足小于等于其中一个,就继续循环 
#include<bits/stdc++.h>
using namespace std;
int a[100000],b[100000],c[100000],lena,lenb,lenc,x;
char n[100000],n1[100000],n2[100000];
string s,m;
int main()
{
    cin>>s>>m;
    lena=s.length();
    lenb=m.length();
    for(int i=0;i<=lena-1;++i)n1[i]=s[i];//tip<2>
    for(int i=0;i<=lenb-1;++i)n2[i]=m[i];
    if(strlen(n1)<strlen(n2)||(strlen(n1)==strlen(n2)&&strcmp(n1,n2)<0))//tip<1> 
    {
    	strcpy(n,n1);
    	strcpy(n1,n2);
    	strcpy(n2,n);
    	printf("-");
	}
	lena=strlen(n1);lenb=strlen(n2);
	for(int i=0;i<=lena-1;++i)a[lena-i]=n1[i]-48;
    for(int i=0;i<=lenb-1;++i)b[lenb-i]=n2[i]-48;
	lenc=1;x=0;
	int x=1;
    while(x<=lena||x<=lenb)//tip<3>
    {
    	if(a[x]<b[x])//借位过程 
    	{
    		a[x]+=10;
    		--a[x+1];
	}
	c[x]=a[x]-b[x];//相减过程 
	++x;
	}
    lenc=x;
    while((c[lenc]==0)&&(lenc>1))--lenc;
    for(int i=lenc;i>=1;--i)printf("%d",c[i]);
    return 0;
}

Part 4 高精度乘法

//part 4 高精度乘法 
//<1>依旧使用竖式,但在乘法中有特殊的规律 
//<2>根据列竖式分析c数组的下标规律,有c[i+j-1]=a[i]*b[i]+x+c[i+j-1],x为进位标记 
//tip<1>:依旧要考虑删除前导的0 
//tip<2>:c的位数就是a的位数+b的位数(可能会有前导0),由乘法的规律可得 
#include<bits/stdc++.h>
using namespace std;
int a[10000],b[10000],c[10000],lena,lenb,lenc,x;
string s,m;
int main()
{
    cin>>s>>m;
    lena=s.length();
    lenb=m.length();
    for(int i=0;i<=lena-1;++i)a[lena-i]=s[i]-48;
    for(int i=0;i<=lenb-1;++i)b[lenb-i]=m[i]-48;
    for(int i=1;i<=lena;++i)
    {
    	x=0;
    	for(int j=1;j<=lenb;++j)
    	{
    		c[i+j-1]=a[i]*b[j]+x+c[i+j-1];
    		x=c[i+j-1]/10;
    		c[i+j-1]%=10;
		}
		c[i+lenb]=x;
	}
    lenc=lena+lenb;//tip<2>
    while(c[lenc]==0&&lenc>1)--lenc;//tip<1>
    for(int i=lenc;i>=1;--i)printf("%d",c[i]);
    return 0;
}

Part 5 高精度除法(低精)

//part 5 高精度除法(低精) 
//<1>在高精度除法中,要涉及到乘法运算和减法运算,以及移位处理,此处采取按位相除法 
//tip<1>:和高精度乘法一样,由于c的最终位数可能会变化,所以有一个删除前导0的过程 
//tip<2>:移位过程中x的算法有所不同
#include<bits/stdc++.h>
using namespace std;
int a[10000],b,c[10000],lena,lenc,x;
string s;
int main()
{
    cin>>s;
    scanf("%d",&b);
    lena=s.length();
    for(int i=0;i<=lena-1;++i)a[i+1]=s[i]-48;
    for(int i=1;i<=lena;++i)
    {
    	c[i]=(x*10+a[i])/b;
    	x=(x*10+a[i])%b;
	}
    lenc=1;
    while(c[lenc]==0&&lenc<lena)++lenc;//删除前导的0 
    for(int i=lenc;i<=lena;++i)printf("%d",c[i]);
    return 0;
}

暂时解决了这么多问题,w同学受益匪浅,毕竟高精度是一个比较冷门的专题

然而g同学的能力也很有限,只能把他知道的教给w同学。好在g同学找到了另一篇不错的博客,供大家参考

点击打开链接

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值