高精度加减法的应用——试解大数之和

一、前言

        我是夏日弥,很高兴看到您来读我的博客,

        这一次小夏将为您讲解一道经典的算法竞赛入门题,使用的方法涉及高精度加法和减法,vector动态数组,这是一种比较容易想得到,却不好代码实现的方法。

        于此同时,我会为您提供另一种代码比较简洁,却不是很好理解的方法——消零法;

        时间有限,本次讲解会比较简洁,还请您包容和理解,让我们正式开始吧!

二、大数之和

        2.1、题目:

大数之和:      

给定一些大整数,请计算其和。

输入格式:

测试数据有多组。对于每组测试数,首先输入一个整数n(n≤100),接着输入n个大整数(位数不超过200)。若n=0则表示输入结束。

输出格式:

对于每组测试,输出n个整数之和,每个结果单独占一行。

输入样例:

2
43242342342342
-1234567654321
0

输出样例:

42007774688021

         2.2、高精度加减法模拟:

#include<bits/stdc++.h>
using namespace std;

const int N =100010;

vector<int> add(vector<int>& A,vector<int>& B)
{
	vector<int> C;
	
	int t = 0;
	for(int i =0;i<A.size()||i<B.size();i++){
		if(i<A.size())t+=A[i];
		if(i<B.size())t+=B[i];
		C.push_back(t%10);
		t/=10;
	}
	if(t)C.push_back(1);
	return C; 
	
}//高精度加法;

bool cmp(vector<int> &A,vector<int> &B)
{	cout<<"cmp函数已执行"<<endl;
	cout<<"此时的ans和a分别是:";
	for(int i =A.size()-1;i >= 0;i--)cout<<A[i];
	cout<<" ";
	for(int i =B.size()-1;i >= 0;i--)cout<<B[i];
	cout<<endl<<endl;
	if(A.size() != B.size()) return A.size() > B.size();//长度不一样时可以直接判断,返回A.size() > B.size()的真假; 
	for(int i = A.size()-1; i >= 0;i--) //末尾往前依次判断; 
		if(A[i] != B[i])return A[i] > B [i];//例如987和986比较,从末尾7和6比,直接返回7>6为真,即a>b为真; 
	return true; //如果两数相等,返回为真; 
}

vector<int> sub(vector<int> &A,vector<int> &B)//该函数必须确保A>=B; 
{
	vector<int> C;//定义答案数组C; 
	for(int i = 0,t = 0; i<A.size();i++)//遍历较长的A 
	{
		t = A[i] - t;//t为临时记录数,初始记为零;//记录A[i]的值; 
		if( i < B.size()) t -= B[i];//如果i没有到达较短的B.size(),将减去B[i]; 
		C.push_back(( t + 10 ) % 10);//很巧妙的进制记录:先+10,如果是负数,就模拟借位,如果不是负数,相当于什么都没做 
		if(t < 0) t = 1;//当前t<0,说明借了位; 
		else t = 0;//当前t>0,说明没有借位; 
	}
	
	while(C.size() > 1 && C.back() == 0)C.pop_back();//头部消零,C.size()>1 确保了C里面不是一位数,如果 C 是0的话是不用消去的;
	 
	
	return C;
}//高精度减法函数,返回值为vector<int>数组类型; 


vector<int> trans(string a){
	vector<int> C;
	for(int i=a.size()-1;i>=0;i--)C.push_back(a[i]-'0');
	return C;
}



int main(){
	int n;
	
	while(cin>>n && n!=0 ){
		
	vector<int> ans={0};//每一次都定义新的ans; 
	bool ans_bool = 1;//新的ans_bool的状态为 正; 
	bool a_bool = 1;//新的a_bool的状态为 正; 
	
		for(int j = 0; j<n ;j++){
		string a;
		cin>>a;//输入部分; 
		
		vector<int> A ;
		
		if(a[0] == '-'){
			a_bool = 0;//如果是负数,更新a_bool为负数;
			
			for(int i = a.size()-1;i>0;i--)A.push_back(a[i]-'0');
		}
		else{
		a_bool = 1;//如果是正数,更新a_bool为正数;
		A = trans(a);//取a的绝对值; 
		}
		
		//取值部分&判断部分; 
		
		
		cout<<"这里将输出ans_bool 和a_bool 的值,分别是 "<<ans_bool<<" "<<a_bool<<endl<<endl; 
		
		cout<<"这里将输出cmp(ans,A)的结果:  "<<cmp(ans,A)<<endl<<endl;
		
		if(cmp(ans,A)){
			cout<<"计算部分已执行"<<endl<<endl; 
			if(ans_bool && !a_bool){
				cout<<"此函数已经执行1"<<endl<<endl;
				ans = sub(ans,A);
				ans_bool = 1; 
			}
			else if(ans_bool && a_bool){
				cout<<"两数均为正数,已执行2"<<endl;
				ans = add(ans , A);
				ans_bool = 1; 
			}
			else if(!ans_bool && a_bool){
				cout<<"这个部分已被执行;3"<<endl<<endl;
				ans = sub(ans,A);
				ans_bool = 0;
			}
			else if(!ans_bool && !a_bool){
				ans = add(ans,A);
				ans_bool = 0;
			}
			
		}//这个部分是ans>A 
		else{
			if(ans_bool && !a_bool){
				cout<<"此函数已经执行4"<<endl<<endl;
				ans = sub(A,ans);
				ans_bool = 0; 
			}
			else if(ans_bool && a_bool){
				cout<<"两数均为正数,已执行5"<<endl;
				ans = add(ans , A);
				ans_bool = 1; 
			}
			else if(!ans_bool && a_bool){
				cout<<"此函数被执行 6"<<endl; 
				ans = sub(A,ans);
				ans_bool = 1;
			}
			else if(!ans_bool && !a_bool){
				cout<<"此函数被执行 7  "<<endl; 
				ans = add(ans,A);
				ans_bool = 0;
			}
			
		}//这个部分是A>ans; 
		
		//比较部分:ans和A到底谁大?
		
		}
		cout<<endl<<"这里为输出部分,看的是ans的值"<<endl; 
		if(ans_bool){
			cout<<"此时的ans应该为正数: "<<endl; 
		for(int i = ans.size()-1;i>=0;i--)cout<<ans[i];
		cout<<endl; 
		}else{
			cout<<"此时的ans应该为负数:"<<endl;
			cout<<"-";
			for(int i = ans.size()-1;i>=0;i--)cout<<ans[i];
			cout<<endl;
		}
	}
}

        我们拆成几个小问题来逐一破解:

        1,实现高精度加法模板:

vector<int> add(vector<int>& A,vector<int>& B)
{
	vector<int> C;//定义答案数组; 
	
	int t = 0;//定义临时变量t,储存位数的加法结果,如123+29中的3 + 9 = 12;t = 12;  
	for(int i =0;i<A.size()||i<B.size();i++){//遍历加数A和加数B 
		if(i<A.size())t+=A[i]; 
		if(i<B.size())t+=B[i];/* 做位数加法操作, 
		如 123 + 29 中的 3 和 9,t+=3,t+=9,t = 12; 
		i 从末尾开始向前走; 
		i走到123 中的 1 时, 29 没有对应的百位,所以 t += 1; 
		
		*/ 
		C.push_back(t%10);//对t求模,然后用vector的push_back()函数将其压入C的最后一位; 
		t/=10;//自除,得到进制;如t = 12 ,t > 10,t/=10, t = 1;  
	}
	if(t)C.push_back(1);//特判:如果t = 1,也就是最高位进位,直接压入C的最后一位,如999 + 1 = 000,此时t = 1,压入1使其变为 1000; 
	return C; 
	
}//高精度加法;

        2、实现高精度减法模板:

bool cmp(vector<int> &A,vector<int> &B)
{	cout<<"cmp函数已执行"<<endl;
	cout<<"此时的ans和a分别是:";
	for(int i =A.size()-1;i >= 0;i--)cout<<A[i];
	cout<<" ";
	for(int i =B.size()-1;i >= 0;i--)cout<<B[i];
	cout<<endl<<endl;
	if(A.size() != B.size()) return A.size() > B.size();//长度不一样时可以直接判断,返回A.size() > B.size()的真假; 
	for(int i = A.size()-1; i >= 0;i--) //末尾往前依次判断; 
		if(A[i] != B[i])return A[i] > B [i];//例如987和986比较,从末尾7和6比,直接返回7>6为真,即a>b为真; 
	return true; //如果两数相等,返回为真; 
}

vector<int> sub(vector<int> &A,vector<int> &B)//该函数必须确保A>=B; 
{
	vector<int> C;//定义答案数组C; 
	for(int i = 0,t = 0; i<A.size();i++)//遍历较长的A 
	{
		t = A[i] - t;//t为临时记录数,初始记为零;//记录A[i]的值; 
		if( i < B.size()) t -= B[i];//如果i没有到达较短的B.size(),将减去B[i]; 
		C.push_back(( t + 10 ) % 10);//很巧妙的进制记录:先+10,如果是负数,就模拟借位,如果不是负数,相当于什么都没做 
		if(t < 0) t = 1;//当前t<0,说明借了位; 
		else t = 0;//当前t>0,说明没有借位; 
	}
	
	while(C.size() > 1 && C.back() == 0)C.pop_back();//头部消零,C.size()>1 确保了C里面不是一位数,如果 C 是0的话是不用消去的;
	 
	
	return C;
}//高精度减法函数,返回值为vector<int>数组类型;

        高精度减法比较复杂,因为要判定负数的情况,

        我们写的这个模板仅仅限于A >= B 的情况;

        负数其实应该是一个...在输入输出被考虑的部分:

        我们的加减法本质是 先根据加数的正负计算它们绝对值的和或差

        再判断 结果的正负

        比如说我们计算 1 - 99 = ?

        我们实际上把它变成了 99 - 1 = 98;

        再判定 1 为正数, -99 为负数 ,1 的绝对值比 99的绝对值要大,所以取负号;

         变成 -98;

         3、将string转变成vector<int>

        

vector<int> trans(string a){
	vector<int> C;
	for(int i=a.size()-1;i>=0;i--)C.push_back(a[i]-'0');
	return C;
}

        倒序转换,没什么可说的,值得注意的就是 a[i] 是 char型,这里我们采取

        a[i] - '0' 的方式转变成 int 类型 如: '9' - '0' = 9;

        4、处理输入输出部分:

#include<bits/stdc++.h>
using namespace std;
int main(){
	int n;
	while(cin>>n && n!=0){
		
	}//多组输入,直到输入0结束; 
}

        最轻松的部分

        5、正负号 以及 绝对值大小判定:

        小夏的做法是先判定绝对值大小,再判定正负号:

        

int main(){
	int n;
	
	while(cin>>n && n!=0 ){
		
	vector<int> ans={0};//每一次都定义新的ans; 
	bool ans_bool = 1;//新的ans_bool的状态为 正; 
	bool a_bool = 1;//新的a_bool的状态为 正; 
	
		for(int j = 0; j<n ;j++){
		string a;
		cin>>a;//输入部分; 
		
		vector<int> A ;
		
		if(a[0] == '-'){
			a_bool = 0;//如果是负数,更新a_bool为负数;
			
			for(int i = a.size()-1;i>0;i--)A.push_back(a[i]-'0');
		}
		else{
		a_bool = 1;//如果是正数,更新a_bool为正数;
		A = trans(a);//取a的绝对值; 
		}
		
		//取值部分&判断部分; 
		
		
		cout<<"这里将输出ans_bool 和a_bool 的值,分别是 "<<ans_bool<<" "<<a_bool<<endl<<endl; 
		
		cout<<"这里将输出cmp(ans,A)的结果:  "<<cmp(ans,A)<<endl<<endl;
		
		if(cmp(ans,A)){
			cout<<"计算部分已执行"<<endl<<endl; 
			if(ans_bool && !a_bool){
				cout<<"此函数已经执行1"<<endl<<endl;
				ans = sub(ans,A);
				ans_bool = 1; 
			}
			else if(ans_bool && a_bool){
				cout<<"两数均为正数,已执行2"<<endl;
				ans = add(ans , A);
				ans_bool = 1; 
			}
			else if(!ans_bool && a_bool){
				cout<<"这个部分已被执行;3"<<endl<<endl;
				ans = sub(ans,A);
				ans_bool = 0;
			}
			else if(!ans_bool && !a_bool){
				ans = add(ans,A);
				ans_bool = 0;
			}
			
		}//这个部分是ans>A 
		else{
			if(ans_bool && !a_bool){
				cout<<"此函数已经执行4"<<endl<<endl;
				ans = sub(A,ans);
				ans_bool = 0; 
			}
			else if(ans_bool && a_bool){
				cout<<"两数均为正数,已执行5"<<endl;
				ans = add(ans , A);
				ans_bool = 1; 
			}
			else if(!ans_bool && a_bool){
				cout<<"此函数被执行 6"<<endl; 
				ans = sub(A,ans);
				ans_bool = 1;
			}
			else if(!ans_bool && !a_bool){
				cout<<"此函数被执行 7  "<<endl; 
				ans = add(ans,A);
				ans_bool = 0;
			}
			
		}//这个部分是A>ans; 
		
		//比较部分:ans和A到底谁大?
		
		}
		cout<<endl<<"这里为输出部分,看的是ans的值"<<endl; 
		if(ans_bool){
			cout<<"此时的ans应该为正数: "<<endl; 
		for(int i = ans.size()-1;i>=0;i--)cout<<ans[i];
		cout<<endl; 
		}else{
			cout<<"此时的ans应该为负数:"<<endl;
			cout<<"-";
			for(int i = ans.size()-1;i>=0;i--)cout<<ans[i];
			cout<<endl;
		}
	}
}

        小夏为您注释了所有的8种情况;

        小夏使用的是 ans_bool 和 a_bool 来分别判断 ans 和 a 的正负:1 为正 , 0 为负;

        6、连接所有模板

        缝合怪,启动!

        连接上面的所有模板,再删除注释,最终的程序如下:

        最终代码

#include<bits/stdc++.h>
using namespace std;

const int N =100010;

vector<int> add(vector<int>& A,vector<int>& B)
{
	vector<int> C;
	
	int t = 0;
	for(int i =0;i<A.size()||i<B.size();i++){
		if(i<A.size())t+=A[i];
		if(i<B.size())t+=B[i];
		C.push_back(t%10);
		t/=10;
	}
	if(t)C.push_back(1);
	return C; 
	
}//高精度加法;

bool cmp(vector<int> &A,vector<int> &B)
{	
	if(A.size() != B.size()) return A.size() > B.size();//长度不一样时可以直接判断,返回A.size() > B.size()的真假; 
	for(int i = A.size()-1; i >= 0;i--) //末尾往前依次判断; 
		if(A[i] != B[i])return A[i] > B [i];//例如987和986比较,从末尾7和6比,直接返回7>6为真,即a>b为真; 
	return true; //如果两数相等,返回为真; 
}

vector<int> sub(vector<int> &A,vector<int> &B)//该函数必须确保A>=B; 
{
	vector<int> C;//定义答案数组C; 
	for(int i = 0,t = 0; i<A.size();i++)//遍历较长的A 
	{
		t = A[i] - t;//t为临时记录数,初始记为零;//记录A[i]的值; 
		if( i < B.size()) t -= B[i];//如果i没有到达较短的B.size(),将减去B[i]; 
		C.push_back(( t + 10 ) % 10);//很巧妙的进制记录:先+10,如果是负数,就模拟借位,如果不是负数,相当于什么都没做 
		if(t < 0) t = 1;//当前t<0,说明借了位; 
		else t = 0;//当前t>0,说明没有借位; 
	}
	
	while(C.size() > 1 && C.back() == 0)C.pop_back();//头部消零,C.size()>1 确保了C里面不是一位数,如果 C 是0的话是不用消去的;
	 
	
	return C;
}//高精度减法函数,返回值为vector<int>数组类型; 


vector<int> trans(string a){
	vector<int> C;
	for(int i=a.size()-1;i>=0;i--)C.push_back(a[i]-'0');
	return C;
}



int main(){
	int n;
	
	while(cin>>n && n!=0 ){
		
	vector<int> ans={0};//每一次都定义新的ans; 
	bool ans_bool = 1;//新的ans_bool的状态为 正; 
	bool a_bool = 1;//新的a_bool的状态为 正; 
	
		for(int j = 0; j<n ;j++){
		string a;
		cin>>a;//输入部分; 
		
		vector<int> A ;
		
		if(a[0] == '-'){
			a_bool = 0;//如果是负数,更新a_bool为负数;
			
			for(int i = a.size()-1;i>0;i--)A.push_back(a[i]-'0');
		}
		else{
		a_bool = 1;//如果是正数,更新a_bool为正数;
		A = trans(a);//取a的绝对值; 
		}
		
		//取值部分&判断部分; 
		
		
		
		if(cmp(ans,A)){

			if(ans_bool && !a_bool){

				ans = sub(ans,A);
				ans_bool = 1; 
			}
			else if(ans_bool && a_bool){

				ans = add(ans , A);
				ans_bool = 1; 
			}
			else if(!ans_bool && a_bool){

				ans = sub(ans,A);
				ans_bool = 0;
			}
			else if(!ans_bool && !a_bool){
				ans = add(ans,A);
				ans_bool = 0;
			}
			
		}//这个部分是ans>A 
		else{
			if(ans_bool && !a_bool){

				ans = sub(A,ans);
				ans_bool = 0; 
			}
			else if(ans_bool && a_bool){

				ans = add(ans , A);
				ans_bool = 1; 
			}
			else if(!ans_bool && a_bool){

				ans = sub(A,ans);
				ans_bool = 1;
			}
			else if(!ans_bool && !a_bool){

				ans = add(ans,A);
				ans_bool = 0;
			}
			
		}//这个部分是A>ans; 
		
		//比较部分:ans和A到底谁大?
		
		}

		if(ans_bool){

		for(int i = ans.size()-1;i>=0;i--)cout<<ans[i];
		cout<<endl; 
		}else{

			cout<<"-";
			for(int i = ans.size()-1;i>=0;i--)cout<<ans[i];
			cout<<endl;
		}
	}
}

        2.3、消零法

        先加减再消去前导零:

        直接放上成果吧:

        值得注意的是部分编译器会提示数组越界,但小夏没查过是哪一部分越了界,懒狗

#include<bits/stdc++.h>
using namespace std;
string sum(string a,string b)
{
    for(int i=201;i>=0;i--)
    {
        a[i]=a[i]+b[i]-'0';
        if(a[i]>'9'){
            a[i]-=10;
            a[i-1]++;
        }
    }
    return a;
}
string opp(string a){
    for(unsigned int i=0;i<a.length();i++)
    a[i]='9'-a[i]+'0';
    a=sum(a,string(201,'0')+'1');
    return a;
}
int main()
{
    string a,b;
    int n;
    while(cin>>n&&n!=0)
    {
        cin>>a;
        if(a[0]=='-')
        {
            a=a.substr(1);
            a=string(202-a.length(),'0')+a;
            a=opp(a);
        }
        else
        a=string(202-a.length(),'0')+a;
        for(int i=0;i<n-1;i++)
        {
            cin>>b;
            if(b[0]=='-')
        {
            b=b.substr(1);
            b=string(202-b.length(),'0')+b;
            b=opp(b);
        }
            else
        b=string(202-b.length(),'0')+b;
            a=sum(a,b);
        }
        if(a[0]=='9')
        {
            a=opp(a);
            cout<<"-";
        }
        int p=a.find_first_not_of('0');
    if(p==-1)
        cout<<"0"<<endl;
    else
        cout<<a.substr(p)<<endl;
    }
    return 0;
}

三、尾声

        草草写的题解;

        还请您多加包涵;

     

                                                                                                                                    夏日弥死傲娇

                                                                                                                                        2021.12.2

  • 6
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值