前言
內容純屬原創,如有雷同,純屬巧合!
如轉載請注明出處!
參考題目
洛谷
https://www.luogu.com.cn/problem/P1601
OpenJudge
http://noi.openjudge.cn/ch0106/10/
一、高精度的普及內容
在C++中,數據類型最高也就只可定義19位數字(好像吧),但Python不同,Python自帶高精度,這也是比賽不支持Python的一部分原因。
但是自從用了高精度之后,麻麻再也不用擔心上限不夠了!
高精度可以隨意定義位數
二、高精度所涉及的基礎內容
1.高精度所涉及的基礎內容有:數組、字符串、以及基礎計算等
2.涉及很多基礎,如for循環、while循環、if、以及一些邏輯(讓人頭疼的瞎搞),不過相信大家這些都沒有問題(除了我)。
三、基本邏輯和思想
我們先以加法做例子(加法:我不要做栗子!我要……“唉嗎真香!”來自作者稚嫩可愛的吃飯聲)
咳咳,
我們先來手動算一個加法算式(別告訴我你不會算)
好的我們來分析一下(回顧一下小學豎式加法的做法)
右起第1位,3+8=11,寫1進1
右起第2位,3+8=11,寫2進1(本位的1加上進位的1得出2)
右起第3位,3+1=4,寫5(本位的4加上進位的1得出5)
右起第4位,2+4=6
右起第5位,5=5
但是要注意,在C++里可不能像第5位這樣,因為這樣會越界,所以我們第5位的真正算式是:0+5=5
正常時候,我們計算加法豎式有什么要點呢?
來來來我教一下一年級知識(啊不,二年級)
(1)從右邊開始計算
(2)可能會有進位
(3)數位對齊計算
首先我們來看一下,從左邊開始這一條非常簡單,我們只需要存數組的時候倒着存進來,然后輸出倒着輸出就行了
第二條,這也非常簡單,我們只需要將多出來的一位加到后面那一位就行了
第三條更加容易實現,for循環里的i(給你眼神自己體會qwq)
豎式計算到這里
我們來列一下大綱
1.讀入
2.計算
3.輸出
這部分講到這里,具體實現往下看
四、具體邏輯的實現
實現第1步,讀入
但是問題來了,我們這樣去讀,該讀到哪里呢?怎么讀呢?
可能有聰明的老司機小伙伴想到了
可以用
scanf("%1d",a[i]);
來讀進數組中。
可是,這位童鞋,難道你沒有想到,你讀入多長呢?長度咱也不知道哇
所以,我們就要用偉大的萬能的字符串來讀入了
首先我們把字符串名字叫做a1和b1吧(因為我比較習慣把a和b當做數組的名字)
然后cin(其實scanf也可以,這里便於寫(其實是偷懶)才這樣的你肯定沒發現我括號套娃了)
那么字符串的長度我們就可以知道了,
int lena=a1.length(); int lenb=b1.length();
這里我把lena當成a數組的長度,lenb當成b數組的長度
用C++自帶的string.length來獲取長度
當然,你如果不喜歡用string,用char也是可以的,那么你這里就應該寫strlen(r);了
然后我們把字符串轉化成數組元素
for(int i=0;i<lena;i++) a[i]=a1[lena-i]-48;//玄學轉換術 for(int i=0;i<lenb;i++) b[i]=b1[lenb-i]-48;//玄學*2
這里給大家講解一下
-48這個我就不說了,因為48在ASCII碼里代表的是'0',減出來正好可以得出數
上面我們說過了,兩個數組是倒着存的,所以我們這里的lena-i和lenb-i都是輔助我們倒着存的
給大家簡單寫寫
那么我們可以清晰的看出,對比原數組和讀進的數組來看的話,完全就是相反的。
別忘了我們的for+數組也是從右邊開始計算的,這樣讀入部分就算完結了
來實現第2步
我們回顧一下第一張豎式計算的圖片,
最后得出來的結果絕對不可能大於兩個數最大的位數+1
比如999+9999
兩個數中位數最多的是4位
4+1=5
那么這兩個數相加絕對不可能大於5。
那么和的長度有了,就來計算了
我們暫時把和的數組叫做c(其實和存到b也可以,但是我懶的去搞了,就先這樣吧)
這里給大家提一個小問題
我們是應該算完一個數進一次位還是全部算完再進位呢?
A、算完一個數進一次位
B、全部算完再進位
答案是:AB
為什么呢?
比如選A
2 3 4
+ 9 9 9
------------
0 0 1 3
0 1 2 3
1 1 3 3
------------
1 2 3 3
但如果你是存到b數組中的
第一步:計算
2 3 4
+ 9 10 3
第二步:進位
2 3 4
+ 10 3 3
第三步:計算
2 3 4
+ 12 3 3
第四步:進位
2 3 4
+ 1 2 3 3
好的我們暫時計算到這里
開始真正的計算(代碼寫)
首先吧lenc也就是c數組最長長度算出來
int lenc=max(lena,lenb)+1;
然后進行c數組的計算
我們這里用先算完在進位的方法,思路會比較清晰
for(int i=1;i<=lenc;i++){ c[i]=a[i]+b[i]; }
然后是進位部分
這一塊我都不講了,太簡單了,主要講思路
for(int i=1;i<=lenc;i++){ c[i+1]+=c[i]/10; c[i]=c[i]%10; }
還沒結束!
這里提供另一種lenc的計算方法!
在開始定義的地方
也就是
int lenc=max(lena,lenb)+1;
我們可以這樣寫
int lenc=max(lena,lenb);
其實就是少了個+1,但是思路卻大大改變
原先的寫法,是進位就剛好的想法,如果不進位的話數組最前面也就是c[lenc]的位置就會多一個0
而且如果題目上沒說讀入會有前導0的話(前導0就比如0000003333333333333333333333這種數,輸出的時候要特地把前導0過濾一遍)
這種方法需要特意的去除前導0(用while或者if)
while去前導0的寫法我后面會講,if就直接這樣
if(!c[lenc])len--;
但如果用第二種的話,那么思路會大大改變
模擬這道題不進位
然后計算完進位完之后,加一個
if(c[lenc+1])len++;
思路:發現這其實進位了,那么和的長度應該是進位后的長度,也就是原先的lenc+1
不多廢話,我剛剛好像承諾了你們要講while去前導0的
那好吧,我講講
來個例子
0 0 0 1 2 0
這個數要去前導0
那么我們從左邊開始看,左起第一個數是0
去一個前導0位數就減少了1,那么我們直接減位數就行了,也就是lenc--;
0 0 1 2 0
繼續繼續
當前左起第一位發現還是0
去0
0 1 2 0
左起第一位還是0
去0
1 2 0
左起第一位不是0了
那么跳出循環
為啥要跳出循環呢?
不然容易去掉結尾的0,比如原本的120變成了12就錯了
while(!c[lenc]&&lenc>1)lenc--;
前面的!c[lenc]表示這位是0
lenc>1要保證剩個0,不然就啥都不輸出了,比如00000,最后lenc長度就剩下0了。。。。
最后第3步,輸出要進行一個變動
我們讀入的時候是反着讀入的,這意味着我們要反着輸出
那么想到這里就很輕松了
for(int i=lenc;i>=1;i--)cout<<c[i];
這里用的是倒着循環
同樣,你也可以和讀入一樣寫
for(int i=1;i<=lenc;i++)cout<<c[lenc-i];
方法任你選擇,就看個人愛好了
練習題在前面有寫了,也可以自己去洛谷、Openjudge里搜,題非常多。
完整代碼:(頭文件我用的萬能頭,大家要不要養成萬能頭的習慣哦!)
#include<bits/stdc++.h> using namespace std; string a1,b1; int a[1000],b[1000],c[1000]; int main(){ cin>>a1>>b1; int lena=a1.length(); int lenb=b1.length(); for(int i=1;i<=lena;i++) a[i]=a1[lena-i]-48; for(int i=1;i<=lenb;i++) b[i]=b1[lenb-i]-48; int lenc=max(lena,lenb); for(int i=1;i<=lenc;i++){ c[i]=a[i]+b[i]; } for(int i=1;i<=lenc;i++){ c[i+1]+=c[i]/10; c[i]=c[i]%10; } if(c[lenc+1])lenc++; while(c[lenc]==0&&lenc>1)lenc--; for(int i=lenc;i>=1;i--)cout<<c[i]; return 0; }
五、高精度拓展
高精度不僅能實現加法,還可以實現減法、乘法、除法以及求模
這里我簡單講一下運算時的思路
1.減法
lenc我們就直接求被減數和減數的最大位數就行了
我們在計算時,for里先寫一個if,也就是如果被減數比減數大,那么被減數的前一位-1,當前位+10,然后if外面寫減就行了,這個都不用處理進位
但是一般的減法要考慮負數,我們只要在前面讀入完a1和b1的時候這樣寫
if((a1<b1&&a1.length()==b1.length())||a1.length()<b1.length()){
cout<<"-";
string temp;
temp=a;
a=b;
b=temp;
}
這里判斷了兩種情況,一種是兩數位數相等但是大小不一樣,另一種是位數不一樣
然后如果成立的話就把a和b互換,輸出負號
因為被減數小於減數,那么差就相當於負的減數減被減數
比如2-3,他們的差就等於-(3-2)
2.乘法
乘法就很簡單啊
乘法分兩種
高精度*單精度
高精度*高精度
高精度*單精度就相當於加法
比如100*8就很簡單
這里示范高精度*高精度
和加法一樣,不過是需要二重循環來寫
for(int i=1;i<=lena;i++){ for(int j=1;j<=lenb;j++){ c[i+j-1]+=a[i]*b[j]; } }
不過計算要改
因為要很多數相加
比如11*11
那么就是11+110
要很多數相加所以是+=
然后lenc的話就很簡單了
int lenc=lena+lenb+1;
這個lenc大家自己品吧,和加減的差不多個道理
3.除法
除法也分兩種,
高精度/單精度
高精度/高精度
高精度/單精度也很簡單
就是挨個位除而已,除不盡落下來
高精度/高精度就比較麻煩了
先比較位數,在挨個位比較大小
如果能除就除,就相當於比較完大小之后減,但這個減也是高精度-高精度的,減完再看看能不能減,這樣循環,如果不能減了就落下來,下一次循環
但最后他可能會讓你保留小數之類的
那么你就要小心了,要把小數的地方留出來
所以這個地方要謹慎
5.取模
這個大概和除法沒啥區別吧。。。。不就是把余數留出來嗎
反而比除法簡單
都不用留出小數的空間了
六、總結
碼了好半天,真累啊qwq
求給個頂或者占樓ddd
咳咳,那么正式總結一下
我們這里用了字符串轉數字、挨個位計算之類的
總之會了一個,其他幾個想想也就能算出來
易錯點:
(1)保留小數
(2)考慮負數
(3)進位借位
(4)數據范圍(數組大小)
(5)lenc的長度
(6)位數
(7)去不去(或者有沒有)前導0
(8)倒着輸入、輸出
(9)i的起始位置要確定
那么今天到這里,歡迎下次再來看qwq