当前位置:首页 » 《随便一记》 » 正文

C++:第十五讲高精度算法

14 人参与  2024年02月15日 12:36  分类 : 《随便一记》  评论

点击全文阅读


每日C++知识

 system("color xx);是改变字体及背景颜色,前一个x代表一个数字,可以改变背景颜色,后一个x代表一个数字,可以改变字体颜色 ,但都是根据颜色表来的。

记住:要加头文件:#include<windows.h>

颜色对照表: 
0 = 黑色
1 = 蓝色
2 = 绿色
3 = 湖蓝色
4 = 红色
5 = 紫色
6 = 黄色
7 = 白色
8 = 灰色
9 = 亮蓝色
A = 亮绿色
B = 亮湖蓝色
C = 亮红色
D = 亮紫色
E = 亮黄色
F = 亮白色 

例子:

#include<bits/stdc++.h>#include<windows.h> using namespace std;int main(){system("color 01");cout<<"1234567890"; return 0;}

输出结果:

 

高精度简介

在C++中当你用int、long long,甚至是unsigned long long 都无法处理的超级巨大数字,你会感到无比痛苦甚至到绝望,那么我们此时就只有一种方法了——高精度算法。我们可以利用程序设计的方法去实现这样的高精度计算。介绍常用的几种高精度计算的方法。本文主要实现的是自然数范围内做运算的 加法、减法、乘法、除法 四种基本高精度运算。

高精度(High Precision)是一种在计算机编程中用于表示和操作大数的技术。在C++中,可以使用高精度思想来处理大数,例如质数、因数分解等。

(1)高精度加法,所用到的算法很简单,其实就是我们小学所学的加法“竖式”计算。将两个加数的对应数位对齐,也就是说个位对个位、十位对十位、百位对百位,对应的数位进行加法操作,有进位的要进位。

(2)高精度加法要将两个加数按对应的位数一位一位地处理,所以在实践中,将两个加数分别用两个数组进行存储,问题就会变得简单。

(3)之前我们已经说过,C++中最大的整数是long long型,最大位数是20位。所以对于10000位的正整数,我们输入的数据类型应该是一个字符串,也就是string型的数据。

例题1洛谷P1303 A*B Problem

题目描述

给出两个非负整数,求它们的乘积。

输入格式

输入共两行,每行一个非负整数。

输出格式

输出一个非负整数表示乘积。

输入输出样例

输入 #1

1
2

输出 #1

2

说明/提示

每个非负整数不超过10的2000次方

解题思路

在讲这道题之前,我有必要先从高精度加/减法来引入一下。

高精度加减法最重要的是什么?当然是对齐数位了!那么,为了达到这一点,我们不惜要先将输入倒序存储,高精乘也是这样。下面,拿一张表格来示意一下高精度加法的运算过程:

20056
+19023
=39079

那么高精乘呢?他们也需要判定位置关系,只不过关系表达式用坐标表示是这样的:a[i]*b[j]==c[i,j]//乘数 * 乘数=乘积。

AC

#include<bits/stdc++.h>using namespace std;char a1[10001],b1[10001];int a[10001],b[10001],i,x,len,j,c[10001];int main (){    cin>>a1>>b1;    int lena=strlen(a1);int lenb=strlen(b1);    for(i=1;i<=lena;i++)a[i]=a1[lena-i]-'0';    for(i=1;i<=lenb;i++)b[i]=b1[lenb-i]-'0';for(i=1;i<=lenb;i++)for(j=1;j<=lena;j++)c[i+j-1]+=a[j]*b[i];    for(i=1;i<lena+lenb;i++)if(c[i]>9){c[i+1]+=c[i]/10;c[i]%=10;}len=lena+lenb;    while(c[len]==0&&len>1)len--;    for(i=len;i>=1;i--)cout<<c[i];return 0; }

例题2洛谷P1480 A/B Problem

题目描述

输入两个整数 a,b,输出它们的商。

输入格式

两行,第一行是被除数,第二行是除数。

输出格式

一行,商的整数部分。

输入输出样例

输入 #1

10
2

输出 #1

5

说明/提示

0≤a≤10的5000次方,1≤b≤10的9次方。

解题思路

这是一道高精除低精模板题。

模拟竖式被除数的每一位加上余数除以被除数并更新余数的过程即可。

不过要注意一下:在数 a 后面拼上一位数 b 是 10a+b。

自己推一下竖式就可以理解了。

记得最后要去除前导零。

AC

#include <iostream>using namespace std;string a, res;long b;int main() {  cin >> a >> b;  long t = 0;  for (int i = 0; i < a.size(); i++) {    res.push_back((t * 10 + a[i] - '0') / b + '0');    t = (t * 10 + a[i] - '0') % b;  }  int i = 0;  while (i + 1 < res.size() && res[i] == '0') i++;  cout << res.substr(i) << endl;}

例题3洛谷P1601 A+B Problem

题目描述

高精度加法,相当于 a+b problem,不用考虑负数

输入格式

分两行输入。a,b≤10的500次方。

输出格式

输出只有一行,代表 a+b 的值。

输入输出样例

输入 #1

1
1

输出 #1

2

输入 #2

1001
9099

输出 #2

10100

说明/提示

20% 的测试数据,0≤a,b≤10的9次方;

40% 的测试数据,0≤a,b≤10的18次方。

解题思路

A+B高精度版其实是一道模板题,在很多领域都有用处。

高精度算法,属于处理大数字的数学计算方法。在一般的科学计算中,会经常算到小数点后几百位或者更多,当然也可能是几千亿几百亿的大数字。一般这类数字我们统称为高精度数,高精度算法是用计算机对于超大数据的一种模拟加,减,乘,除,乘方,阶乘,开方等运算。

AC

#include<stdio.h>#include<string.h>#include<algorithm>using namespace std;void add(char a[], char b[]){    //获取数组长度    int alen = strlen(a), blen = strlen(b), t = 0, i,len;    // 翻转数组,为了低位对齐    reverse(a,a+alen);    reverse(b,b+blen);    // 存放·结果int c[502]={0};    // 取较短的位len = (alen < blen) ? alen : blen;    // 对应位相加for (i = 0; i <len; i++)    {        c[i]=((a[i]+b[i]-'0'-'0')+t);        // t代表进位        t=c[i]/10;        c[i]=c[i]%10;    }    // 剩余位处理while (i<alen)    {        c[i]=t+(a[i]-'0');        t=c[i]/10;        c[i]=c[i]%10;        i++;    }    while (i<blen)    {        c[i]=t+(b[i]-'0');        t=c[i]/10;        c[i]=c[i]%10;        i++;    }    // 最高位进位    if(t!=0)    {        c[i]=t;i++;    }    // 输出for(i=i-1; i >= 0; i--)         printf("%d", c[i]);}int main(){    char a[502]={0}, b[502]={0};scanf("%s %s", a, b);    add(a,b);    return 0;}

课后练习P1080 [NOIP2012 提高组] 国王游戏

题目描述

恰逢 H 国国庆,国王邀请 n 位大臣来玩一个有奖游戏。首先,他让每个大臣在左、右手上面分别写下一个整数,国王自己也在左、右手上各写一个整数。然后,让这 n 位大臣排成一排,国王站在队伍的最前面。排好队后,所有的大臣都会获得国王奖赏的若干金币,每位大臣获得的金币数分别是:排在该大臣前面的所有人的左手上的数的乘积除以他自己右手上的数,然后向下取整得到的结果。

国王不希望某一个大臣获得特别多的奖赏,所以他想请你帮他重新安排一下队伍的顺序,使得获得奖赏最多的大臣,所获奖赏尽可能的少。注意,国王的位置始终在队伍的最前面。

输入格式

第一行包含一个整数 n,表示大臣的人数。

第二行包含两个整数 a 和 b,之间用一个空格隔开,分别表示国王左手和右手上的整数。

接下来 n 行,每行包含两个整数 a 和 b,之间用一个空格隔开,分别表示每个大臣左手和右手上的整数。

输出格式

一个整数,表示重新排列后的队伍中获奖赏最多的大臣所获得的金币数。

输入输出样例

输入 #1

3
1 1
2 3
7 4
4 6 

输出 #1

2

说明/提示

【输入输出样例说明】

按 1、2、3 这样排列队伍,获得奖赏最多的大臣所获得金币数为 2;

按 1、3、2 这样排列队伍,获得奖赏最多的大臣所获得金币数为2;

按 2、1、3 这样排列队伍,获得奖赏最多的大臣所获得金币数为 2;

按2、3、1这样排列队伍,获得奖赏最多的大臣所获得金币数为 9;

按 3、1、2这样排列队伍,获得奖赏最多的大臣所获得金币数为 2;

按3、2、1 这样排列队伍,获得奖赏最多的大臣所获得金币数为 9。

因此,奖赏最多的大臣最少获得 2个金币,答案输出2。

【数据范围】

对于 20%的数据,有 1≤n≤10,0<a,b<8;

对于 40%的数据,有1≤n≤20,0<a,b<8;

对于 60%的数据,有 1≤n≤100;

对于 60%的数据,保证答案不超过 10的9次方;

对于 100%的数据,有 1≤n≤1,000,0<a,b<10000。

解题思路

其实懂了思想还是挺简单的,朴素的高精度乘法和高精度除法,但难想到的还是如何一开始给他们排序,于是冥思苦想,终于发现了如何排序(好吧,借鉴了别人的想法),便是按a*b排序。

为什么呢?我简单说明一下,假设前面几个人都排好了,我们要如何证明最后两个人谁排前面会对答案影响小一点呢?也就是贡献得尽量小一点,设前几个人a的乘积为x,那么这两个人(a1,b1)和(a2,b2)谁排前一个结果就为x*a1/b2和x*a2/b1,将他们做下比较,易得出当a1*b1<a2*b2时(a1,b1)排在前面更好【可以在草稿纸上列下不等式算下,不难得出】,然后就简单了!

AC

#include<bits/stdc++.h>#define ll long long#define ld long doubleusing 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));        //cout<<"ans=="<<ans<<endl;        xx=hmul(xx,tur_str(a[i].l));    }cout<<ans<<endl;    return 0;}

结尾

希望大家多多关注!!!

本篇文章共5996字,如果你能支持一下我,我十分感谢!!!

如果有人想在洛谷上做题,可以点下方链接:

https://www.luogu.com.cn/

如果你喜欢或想了解一下其他的算法,可以看看以下这些:

题目详解系列(部分):

【万题详解】洛谷P1252 马拉松接力赛-CSDN博客

【万题详解】洛谷P1359 租用游艇-CSDN博客

【百题详解】洛谷P8508 做不完的作业-CSDN博客

【万题详解1】洛谷P1230 智力大冲浪-CSDN博客

【全网首发】洛谷贪心题解集合-CSDN博客

洛谷二分题集(3题)-CSDN博客

游戏系列:

C++:史上最坑小游戏-CSDN博客

 C++:自创小游戏-CSDN博客

C++:下雪-CSDN博客

C++讲解系列(算法):

C++:第十二讲DFS深搜(二)_c++匿名函数dfs-CSDN博客

 C++:第十一讲DFS深搜-CSDN博客

C++:第十讲二分查找-CSDN博客

前缀和与差分:

C++:第九讲前缀和与差分-CSDN博客

贪心:

C++:第八讲贪心算法1-CSDN博客

C++讲解系列(基础入门):

排序:

C++:第七讲冒泡排序-CSDN博客

函数:

C++第6讲max和min函数_c++ min函数-CSDN博客

C++第五讲函数初步-CSDN博客

for循环&数组:

C++第四讲for循环及数组-CSDN博客

if语句&else语句及运算:

C++第三讲:C++中的逻辑运算符及if else语句-CSDN博客

基础:

C++第二讲输入与输出-CSDN博客

C++第一讲认识C++编译器-CSDN博客

欢迎收看,希望大家能三连!

最后认识一下,我是爱编程的喷火龙廖,我们有缘再见!


点击全文阅读


本文链接:http://zhangshiyu.com/post/68557.html

<< 上一篇 下一篇 >>

  • 评论(0)
  • 赞助本站

◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。

关于我们 | 我要投稿 | 免责申明

Copyright © 2020-2022 ZhangShiYu.com Rights Reserved.豫ICP备2022013469号-1