一、前言
我是夏日弥,很高兴看到您来读我的博客,
这一次小夏将为您讲解一道经典的算法竞赛入门题,使用的方法涉及高精度加法和减法,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