概念
在我们进行计算的过程中,经常会遇到几十位,甚至几百位的数字的计算问题,也有可能会遇到小数点后几十位,几百位的情况,而我们面对这样的情况下,long long和double的数据范围显然是不够使用的了。因此这时,我们就需要引入一个新的算法,叫做高精度算法
高精度算法本质上是用字符串进行模拟,再利用类似于数学里的竖式的形式,一位一位进行计算
处理
高精度计算中需要处理好以下几个问题:
1.数据的接收方法和存储方法
数据的接收和存储:采用string或char方式输入,利用字符转数字的操作运算,将每一位取出,存入int类型的数组中 .
下面是输入数据的伪代码:
int main(){int a[10005];memset(a,0,sizeof(a));string s;cin >> s; len = s.size();//也可以写成s.length -->其作用是计算字符串位数for(int i=1;i<=len;i++) a[i]=s[len-i]-'0';//将字符串s转换为数组a,倒序存储
}
2.进位、借位的处理.
加法进位: c[i] = a[i] + b[i]
if(c[i]>=10){c[i]%=10;++c[i++];
}
减法借位: c[i] = a[i] - b[i]
if(a[i]<b[i]){--a[i+1];a[i]+=10;
}
乘法进位: 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++ 语言中任何数据类型都有一定的范围. 当两个加数很大时,以前的方法显然不能求出答案(放不下那么大的数据),因此我们需要寻求另一种方法 .在读小学时,我们做加法都采用竖式方法 . 这样我们方便写出两个整数相加的算法.减法也是同样
所以在程序中,我们也可以用模拟的方法求解。我们只要模拟出来竖式的过程即可
以下是高精度加减法的程序:
//加法
#include<bits/stdc++.h>
using namespace std;
string a,b,d;
int j;
int main(){cin>>a>>b;while(a.size()<b.size())a='0'+a;while(a.size()>b.size())b='0'+b;for(int i=a.size()-1;i>=0;i--){if(int(a[i]-'0')+int(b[i]-'0')+j>=10)d=char(int(a[i]-'0')+int(b[i]-'0')+j-10+'0')+d,j=1;else d=char(int(a[i]-'0')+int(b[i]-'0')+'0')+d,j=0;}if(j==1)d='1'+d;cout<<d;return 0;
}
//减法
#include<bits/stdc++.h>
using namespace std;
string s1,s2;
int a[240],b[240],c[240];
char q='+';
int main()
{int p;cin>>s1>>s2;if(s1.size()<s2.size()||(s1.size()==s2.size()&&s1<s2)){q='-';swap(s1,s2);}for(int i=0;i<s1.size();i++)a[i]=s1[s1.size()-i-1]-'0';for(int i=0;i<s2.size();i++)b[i]=s2[s2.size()-i-1]-'0';if(q=='-')cout<<'-';for(int i=0;i<s1.size();i++){if(a[i]<b[i])a[i+1]-=1,a[i]+=10;c[i]=a[i]-b[i];}int len=s1.size();for(int i=len-1;i>=0;i--)if(c[i]!=0){p=i;break;}for(int i=p;i>=0;i--)cout<<c[i];return 0;
}
高精度乘法
类似加法,使用竖式。在做乘法时,同样也有进位
乘法下标规律:
c[i+j−1]=a[i]+b[j]+x+c[i+j−1]
#include<bits/stdc++.h>
using namespace std;
char a1[10000],a2[100000];
int a[10000],b[10000],c[10000];
int len_a,len_b,len_c=1;
int main(){cin>>a1>>a2;len_a=strlen(a1);len_b=strlen(a2);for(int i=1;i<=len_a;i++)a[i]=a1[len_a-i]-'0';for(int i=1;i<=len_b;i++)b[i]=a2[len_b-i]-'0';for (int i=1;i<=len_a;i++)for(int j=1;j<=len_b;j++)c[i+j-1]+=a[i]*b[j];for(int i=1;i<len_a+len_b;i++)if(c[i]>9)c[i+1]+=c[i]/10,c[i]%=10;int len=len_a+len_b;while(c[len]==0&&len>1)len--;for(int i=len;i>=1;i--)cout<<c[i];return 0;
}
高精度除法
除法高精度分两种情况:
①高精除以低精
②高精除以高精
但是我们想想,我们可不可以把低精看成高精去计算呢?
所以不管是低精还是高精,我们都可以看成都是高精度去模拟计算
高精除以高精使用减法模拟除法,对被除数的每一位都减去除数,一直减到当前位置的数字
代码如下:
#include<bits/stdc++.h>
using namespace std;
int dividend[5000] = {0};
int quotient[5000] = {0};
int main() {string a;int divisor;cin >> a >> divisor;int al = a.length();for (int i = 0; i < al; i++){dividend[i] = a[i]-'0';}long long remainder = 0;for (int i = 0;i < al;i++){ quotient[i] = (remainder * 10 + dividend[i]) / divisor;remainder = remainder * 10 + dividend[i] - (quotient[i] * divisor);}int zeroflag = 0;for (int i = 0; i < al ; i++){if (quotient[i] == 0 && zeroflag == 0 && i < al-1){continue;}zeroflag = 1;cout << quotient[i];}return 0;
}
习题
计算N的阶乘
请计算n的阶乘(1<=n<=100)
n的阶乘计算公式为:n!=n*(n-1)*(n-2)*...*1,如:5!=5*4*3*2*1=120
输入
一个整数n(1<=n<=100)
输出
n的阶乘
样例输入
20
样例输出
2432902008176640000
代码:
#include<bits/stdc++.h>
using namespace std;
int n,l=1,a[100000]={0,1};
int main(){cin>>n;for(int i=1;i<=n;i++){for(int j=1;j<=l;j++)a[j]*=i;for(int j=1;j<=l;j++)if(a[j]>=10)a[j+1]+=a[j]/10,a[j]%=10;while(a[l+1])l++,a[l+1]=a[l]/10,a[l]%=10;}for(int i=l;i>=1;i--)cout<<a[i];return 0;
}
破译密码
我军拦截敌军传递了2个数字串,后经侦查得知,这两个数字串中包含了一个很重要的密码,这个密码的破译方式为,用这两个数字串中的大数 – 小数,得到结果后将结果转换为16进制(转换时如需使用字母请使用大写字母),就是敌军想要传递的密码。
比如,敌军传递了下列两个数字串:
99999999999999999999999999999999999999973
99999999999999999999999999999999999999999
大数 – 小数的差值 = 26,转换为16进制的结果为1A,也就是1A就是敌军想要传递的密码。
请编程实现破译密码的过程。
输入
输入有2行,分别是2个不超过200位的整数,且已知2个整数的差是一个不超过18位的整数。
输出
输出按题意计算出的16进制数。
样例输入
99999999999999999999999999999999999999973
99999999999999999999999999999999999999999
样例输出
1A
代码:
#include<bits/stdc++.h>
using namespace std;
string s1,s2,s;
long long a[240],b[240],c[240],sum,p;
int main()
{cin>>s1>>s2;if(s1.size()<s2.size()||(s1.size()==s2.size()&&s1<s2))swap(s1,s2);for(long long i=0;i<s1.size();i++)a[i]=s1[s1.size()-i-1]-'0';for(long long i=0;i<s2.size();i++)b[i]=s2[s2.size()-i-1]-'0';for(long long i=0;i<s1.size();i++){if(a[i]<b[i])a[i+1]-=1,a[i]+=10;c[i]=a[i]-b[i];}long long len=s1.size();for(long long i=len-1;i>=0;i--){if(c[i]!=0){p=i;break;}}for(long long i=p;i>=0;i--)sum=sum*10+c[i];if(sum==0){cout<<0;return 0;}while(sum){if(sum%16<=9)s=char(sum%16+'0')+s;else s=char(sum%16-10+'A')+s;sum/=16;}cout<<s;return 0;
}
洛谷P2687
“逢低吸纳”是炒股的一条成功秘诀。如果你想成为一个成功的投资者,就要遵守这条秘诀:
"逢低吸纳,越低越买"
这句话的意思是:每次你购买股票时的股价一定要比你上次购买时的股价低.按照这个规则购买股票的次数越多越好,看看你最多能按这个规则买几次。
给定连续的N天中每天的股价。你可以在任何一天购买一次股票,但是购买时的股价一定要比你上次购买时的股价低。写一个程序,求出最多能买几次股票。
以下面这个表为例, 某几天的股价是:
天数 1 2 3 4 5 6 7 8 9 10 11 12
股价 68 69 54 64 68 64 70 67 78 62 98 87
这个例子中, 聪明的投资者(按上面的定义),如果每次买股票时的股价都比上一次买时低,那么他最多能买4次股票。一种买法如下(可能有其他的买法):
天数 2 5 6 10
股价 69 68 64 62
输入格式
第1行: N (1 <= N <= 5000), 表示能买股票的天数。
第2行以下: N个正整数 (可能分多行) ,第i个正整数表示第i天的股价. 这些正整数大小不会超过longint(pascal)/long(c++).
输出格式
只有一行,输出两个整数:
能够买进股票的天数和长度达到这个值的股票购买方案数量
在计算方案的数量的时候,如果两个方案的股价序列相同,那么这样的两个方案被认为是相同的(只能算做一个方案)。因此,两个不同的天数序列可能产生同一个股价序列,这样只能计算一次。
样例输入
12
68 69 54 64 68 64 70 67
78 62 98 87
样例输出
4 2
代码:
#include<bits/stdc++.h>
using namespace std;
int dp[5001];
int a[5001];
int a1[110],b1[110],c1[110];
//int num[5001];
struct node{int len,zu[110];node operator + (const node x)const{memset(a1,0,sizeof(a1));memset(b1,0,sizeof(b1));memset(c1,0,sizeof(c1));node res;res.len=0;for(int i=1,j=len;i<=len;i++,j--)a1[i]=zu[j];for(int i=1,j=x.len;i<=x.len;i++,j--)b1[i]=x.zu[j];for(int i=1;i<=max(len,x.len);i++){c1[i]+=a1[i]+b1[i];c1[i+1]+=c1[i]/10;c1[i]=c1[i]%10;}int l=max(len,x.len);while(c1[l+1]){l++;c1[l+1]=c1[l]/10;c1[l]=c1[l]%10;}res.len=l;for(int i=1,j=l;i<=l;i++,j--)res.zu[i]=c1[j];return res;}
}num[5001];
int main()
{int n;cin>>n;for(int i=0;i<n;i++){cin>>a[i];}for(int i=0;i<=n;i++){num[i].len=1;num[i].zu[1]=1;int tmp=1<<30;for(int j = i-1; j>=0;j--)if(a[j]>a[i]){if(dp[j]>= dp[i]){tmp = a[j];dp[i]= dp[j]+1;num[i]= num[j];}else if (dp[j]+1 == dp[i]&& a[j]< tmp){tmp = a[j];num[i]=num[i]+num[j];}}}printf("%d ",dp[n]);for(int i=1;i<=num[n].len;i++)printf("%d",num[n].zu[i]);
}
国王游戏
恰逢 H 国国庆,国王邀请 n 位大臣来玩一个有奖游戏。首先,他让每个大臣在左、右手上面分别写下一个整数,国王自己也在左、右手上各写一个整数。然后,让这 n 位大臣排成一排,国王站在队伍的最前面。排好队后,所有的大臣都会获得国王奖赏的若干金币,每位大臣获得的金币数分别是:排在该大臣前面的所有人的左手上的数的乘积除以他自己右手上的数,然后向下取整得到的结果。
国王不希望某一个大臣获得特别多的奖赏,所以他想请你帮他重新安排一下队伍的顺序,使得获得奖赏最多的大臣,所获奖赏尽可能的少。注意,国王的位置始终在队伍的最前面。
输入格式
第一行包含一个整数 n,表示大臣的人数。
第二行包含两个整数 a 和 b,之间用一个空格隔开,分别表示国王左手和右手上的整数。
接下来 n 行,每行包含两个整数 a 和 b,之间用一个空格隔开,分别表示每个大臣左手和右手上的整数。
输出格式
一个整数,表示重新排列后的队伍中获奖赏最多的大臣所获得的金币数。
样例输入
3
1 1
2 3
7 4
4 6
样例输出
2
说明/提示
【输入输出样例说明】
按 11、22、33 这样排列队伍,获得奖赏最多的大臣所获得金币数为 22;
按 11、33、22 这样排列队伍,获得奖赏最多的大臣所获得金币数为 22;
按 22、11、33 这样排列队伍,获得奖赏最多的大臣所获得金币数为 22;
按22、33、11这样排列队伍,获得奖赏最多的大臣所获得金币数为 99;
按 33、11、22这样排列队伍,获得奖赏最多的大臣所获得金币数为 22;
按33、22、11 这样排列队伍,获得奖赏最多的大臣所获得金币数为 99。
因此,奖赏最多的大臣最少获得 22 个金币,答案输出 22。
【数据范围】
对于 20%20% 的数据,有 1≤n≤10,0<a,b<8
对于 40%40% 的数据,有1≤n≤20,0<a,b<8;
对于 60%60% 的数据,有1≤n≤100;
对于 60%60% 的数据,保证答案不超过 109;
对于 100%100% 的数据,有 1≤n≤1,000,0<a,b<10000。
代码:
#include<bits/stdc++.h>
#define ll long long
#define ld long double
using namespace std;
string hmul(string a,string b){int c[500005];memset(c,0,sizeof c);int x[a.size()],y[b.size()];memset(x,0,sizeof x);memset(y,0,sizeof y);for(unsigned int i=0;i<a.size();i++)x[a.size()-1-i]=a[i]-'0';for(unsigned int i=0;i<b.size();i++)y[b.size()-i-1]=b[i]-'0';for(unsigned int i=0;i<a.size();i++){for(unsigned j=0;j<b.size();j++){c[i+j]+=x[i]*y[j];}}for(unsigned int i=0;i<a.size()+b.size();i++){if(c[i]>=10){c[i+1]+=c[i]/10;c[i]%=10;}}string ci;bool p=1;for(int i=a.size()+b.size()-1;i>=0;i--){if(c[i]==0&&p) continue;else{p=0;ci+=c[i]+'0';}}return ci;
}
string hdiv(string a,int b){int x[50005];int y[50005];memset(x,0,sizeof(x));memset(y,0,sizeof(y));for(unsigned int i=0;i<a.size();i++){x[i+1]=a[i]-'0';}int yu=0;for(unsigned int i=1;i<=a.size();i++){y[i]=(yu*10+x[i])/b;yu=(yu*10+x[i])%b;}int kk=1;while(y[kk]==0&&kk<a.size()) kk++;string aa;for(unsigned int i=kk;i<=a.size();i++){aa+=y[i]+'0';}return aa;
}
string smx(string a,string b){if(a.size()!=b.size()) return a.size()>b.size()?a:b;return a>b?a:b;
}
string tur_str(int num){string str;while(num){str+=num%10+'0';num/=10;}reverse(str.begin(),str.end());return str;
}
int n;
struct aa{int l,r;
}a[100005];
bool cmp(aa a,aa b){return (a.l*a.r)<(b.l*b.r);
}
int main(){cin>>n;cin>>a[0].l>>a[0].r;for(int i=1;i<=n;i++) cin>>a[i].l>>a[i].r;sort(a+1,a+n+1,cmp);string ans="0";string xx=tur_str(a[0].l);for(int i=1;i<=n;i++){ans=smx(ans,hdiv(xx,a[i].r));xx=hmul(xx,tur_str(a[i].l));}cout<<ans<<endl;return 0;
}