C++信息學(xué)競(jìng)賽例題解析——高精度加法

近期呢,我們剛學(xué)了C++中string字符串的基礎(chǔ)與應(yīng)用。string字符串的一大好處就是輸入的格式?jīng)]有限制,長(zhǎng)度不限。并且相較于C語(yǔ)言的char,總體而言方便了不少。當(dāng)數(shù)據(jù)大小可能超過(guò)long long int的數(shù)據(jù)范圍或者小數(shù)后面的位數(shù)超過(guò)雙精度浮點(diǎn)數(shù)的時(shí)候,通過(guò)字符串實(shí)現(xiàn)的“高精度”計(jì)算就是一個(gè)很好的解決方案。在我們做真正的競(jìng)賽題時(shí),可能會(huì)遇到超大的輸入數(shù)據(jù)規(guī)模,這時(shí)候使用這種高精度數(shù)據(jù)的計(jì)算就變得很有必要。

歷經(jīng)本UP主3天3夜的思考+爆肝,我終于弄明白了這樣的一道題的解法。

我的解決方案:


實(shí)際上這個(gè)題的難點(diǎn)有這么幾個(gè):
要讓你的程序判定輸入數(shù)據(jù)是否有小數(shù)點(diǎn)并且做出相應(yīng)反饋。
要把小數(shù)和整數(shù)分開(kāi)計(jì)算,但是小數(shù)到整數(shù)的進(jìn)位問(wèn)題是不可以忽視的。
根據(jù)我個(gè)人的習(xí)慣以及節(jié)約時(shí)間的原則,我會(huì)把計(jì)算用的數(shù)組給提前開(kāi)辟好,在主程序開(kāi)始前將需要的內(nèi)存空間申請(qǐng)下來(lái)。但是再輸出的時(shí)候我需要保證不輸出多余的“0”
小數(shù)中的十分位進(jìn)位到個(gè)位是一大難點(diǎn)。
我的程序詳解
#include <iostream>
#include <string>
#include <cstring>
using namespace std;
【5】 bool ata = false,atb = false;
??string pa,pb,pc;
???int y = 0;
??bool sta = false,jin = false;
?int sn[40],ln[40];
【10】int main(){
?cin >> pa >> pb;//最基本的,聲明數(shù)組和數(shù)據(jù)輸入
?if(pa.find('.')==-1){
??ata = true;
???}??
【15】if(pb.find('.')==-1){?
??atb = true; //確定輸入的數(shù)據(jù)中有無(wú)整數(shù)
??}
?int sc[40][2],lc[40][2];
?int x = 0;
【20】 memset (sc,0,sizeof sc);
?memset (lc,0,sizeof lc);//清空用于計(jì)算的數(shù)組
? int g =pa.find('.'),h=pb.find('.');
?if(ata ==true)
?g = pa.size();
【25】if(atb == true)
?h = pb.size();//確定小數(shù)點(diǎn)在string數(shù)組中的位置

程序至此就算完成了基本的數(shù)據(jù)情況的設(shè)置,接下來(lái),就是核心的計(jì)算環(huán)節(jié)了。
for(int a=g,b=h;a<=pa.size()||b<=pb.size();a++,b++,x++){
??if(pa[a]>='0'&&pa[a]<='9')
??sc[x][0]=pa[a]-'0';
?【30】 if(pb[b]>='0'&&pb[b]<='9')
??sc[x][1]=pb[b]-'0';?? ?????????''
???}
??memset (lc,0,sizeof lc);
?int a =pa.find('.'),b=pb.find('.');
?【35】if(ata ==true)
?a = pa.size();
?if(atb == true)
?b = pb.size();
?for( ;a>=0||b>=0;a--,b--,y++){
?【40】 if(pa[a]>='0'&&pa[a]<='9')
??lc [y][0]=pa[a]-'0';
??if(pb[b]>='0'&&pb[b]<='9')
??lc[y][1]=pb[b]-'0';
???}
//上面的環(huán)節(jié)是將字符串形式的數(shù)據(jù)轉(zhuǎn)化成便于進(jìn)行計(jì)算加工的數(shù)組形式。先轉(zhuǎn)化的是小數(shù)部分,然后是整數(shù)部分。兩個(gè)部分分別存放在兩個(gè)不同的二維數(shù)組里,準(zhǔn)備運(yùn)算。
?【45】for(int z = 39;z>=0;z--){
??sn[z]=(sc[z][0]+sc[z][1])%10;
??sc[z-1][0]=sc[z-1][0]+((sc[z][0]+sc[z][1])/10);
???}
?if(sn[0]==1){
?【50】jin=true;//bool變量“ jin”反應(yīng)的是小數(shù)是否需要進(jìn)位到整數(shù)
?lc[0][0]=lc[0][0]+1;}
??for(int z = 0;z<=39;z++){
???if(jin==true&&z==0){
????ln[z]=ln[z]+((lc[z][0]+lc[z][1])%10);
?【55】 lc[z+1][0]=lc[z+1][0]+((lc[z][0]+lc[z][1])+1/10);
?????}
??else{
??ln[z]=(lc[z][0]+lc[z][1])%10;
?lc[z+1][0]=lc[z+1][0]+((lc[z][0]+lc[z][1])/10);}
?【60】 }
這里是最中心的運(yùn)算環(huán)節(jié)。記住我們應(yīng)該從最小的一位算起,先算小數(shù),再看是否需要進(jìn)位,再算整數(shù)。此時(shí)答案已經(jīng)被存儲(chǔ)在in和sn兩個(gè)數(shù)組里了。

最后的輸出環(huán)節(jié)中,兩個(gè)數(shù)組分別需要用不同的辦法排除數(shù)組里多余的“0”,然后判斷是否需要輸出小數(shù)點(diǎn)以及后面的數(shù)。
for(int z = 30;z>0;z--){
??if(ln[z]!=0)
??sta = true;
??if(sta == true)
??cout << ln[z];
???}
?if(sta==false)
?cout <<"0"; //輸出整數(shù)的部分
?int j = 29;
while(j>0&&sn[j]==0){
?j = j -1; }//通過(guò)一個(gè)while循環(huán)來(lái)確定輸出多少位小數(shù)
if(j)
cout << ".";//確定輸出小數(shù)點(diǎn)
?for(int a = 1;a<=j;a++){
??cout <<sn[a];
???}//輸出小數(shù),程序結(jié)束。
?return 0; }

由于本題中給的數(shù)據(jù)規(guī)模較小,我可以將程序?qū)懙南鄬?duì)復(fù)雜。但為了貫徹“節(jié)約每一毫秒的運(yùn)行時(shí)間”的原則,我將數(shù)組和各類變量、string字符串的申請(qǐng)放到了主程序開(kāi)始之前——這樣測(cè)評(píng)機(jī)就不會(huì)將調(diào)用內(nèi)存空間的時(shí)間計(jì)入運(yùn)行時(shí)間內(nèi)。
