【2023.03.17】Luogu P1001 A+B Problem 题解

P1001 A+B Problem

在此声明:本人在首A此题时,确实是一个萌新。那么,让我们以萌新的角度,看一下这道题:

首先,题目大意:

输入一个A,再输入一个B,求出A与B的和,输出。

嗯,题目非常有深意,我们不妨来一步一步做:

  1. 头文件:作为萌新,我们必须来认识C++的头文件,头文件定义多少直接关系到我们能够调用多少函数,我们在这里需要的函数只有主函数和 cin 以及 cout 。直接定义iostream库就可以啦!

    #include <iostream>
    
  2. 指明:作为萌新,我们有必要背过并熟练掌握这句语句,Ta相当于一把万能钥匙,可以在我们主函数内调用函数时使用std。如果没有写,那么我们在主函数里调用std的函数时就必须在前面加上std::唷!

    using namespace std;
    
  3. 主函数:终于写主函数了呢,作为萌新,我们一般会用这样的主函数:

    int main(){
        
    }
    

    然而,主函数并不是只有这一种写法,在此再放出两种:

    signed main(){
        
    }
    
    int main(void){
        
    }
    

    我们可以任意用一种呢,不过在main前面,是不能用long long的哦。

  4. 定义:作为萌新,我们自然知道如何定义一个变量:

    类型名 变量名;
    

    不过在此建议,变量名最好有一定的含义,不然容易弄混呢。

    在这里,我们注意看数据范围:

    |a|,|b| ≤ 10 ^ 9

    显然,int类型完全可以驾驭,不过保险起见,我们还是定义成long long类型吧。

    long long A,B;
    
  5. 输入:作为萌新,我们深知cin的魅力,所以在这里我们就用cincout啦!

    cin>>A>>B;
    
  6. 计算 & 输出:作为萌新,我们自然知道在C++中,一部分运算符和我们日常数学上的运算符是一样的,加号就是如此。那么我们直接在输出对象运算就可以啦!

    cout<<A+B;
    
  7. 结束程序:作为萌新,我们一般都具有OI圈中最好的习惯。那么让我们把好习惯发扬光大吧!

    return 0;
    

    Code

#include <iostream>
using namespace std;
int main(){
    long long A,B;
    cin>>A>>B;
    cout<<A+B;
    return 0;
}

P1001 A+B Problem(模拟版)

那么,我们再来看看:这道题还能怎么做呢?

时隔半载,我又点开这道题。与当时不同的是,现在我不再是只会cin的萌新了,既然不是萌新,就要有远大理想。我们来思考下这道题其他方法:

① 线段树:然而蒟蒻还并没有怎么掌握呢

② 树状数组:然而蒟蒻并不会

③ 最小生成树:下面会讲力

④ Link-Cut Tree:蒟蒻还是不会呢

⑤ Splay:呜呜,蒟蒻还是不会力

⑥ Dijkstra:这个还是会一点的,不过下面就不想讲了呢

⑦ 字典树:蒟蒻并不会

⑧ 位运算:下面也会讲力

⑨ 模拟:这是我最喜欢的呢,下面也会讲力

⑩ 快速读入优化:其实本人并不认为这是种解法,但是这位写了,就讲下力

位运算:

先说一下为什么要用位运算:因为位运算的速度远快于普通运算,所以就用位运算力。

大体思路就是模拟十进制竖式加法,先利用异或运算算出不进位的值,再通过与运算算出进位的值。递归调用之后加起来输出就好啦!

那么接下来,我们一步步看吧!

  1. 模板 & 定义 & 输入:

    就不多讲了呢,直接放代码吧:

    #include <bits/stdc++.h>
    using namespace std;
    int main(){
    	//freopen(".in","r",stdin);
        //freopen(".out","w",stdout);
        long long A;
        long long B;
        cin>>A;
        cin>>B;
        //To-Do;
        //fclose(stdin);
        //fclose(stdout);
        return 0;
    }
    
  2. 正题:大体思路已经讲了,那就直接放代码实现吧:

    位运算加法函数:

    int plu(int a,int b){
        if(b==0){
            return a;
        }
        else{
            int xor;
            int car;
            xor=a^b;
            car=(a&b)<<1;
            return plu(xor,car);
        }
    }
    

    这是递归实现呢,直接输出plu(A,B)就好啦!

    Code

    #include <bits/stdc++.h>
    using namespace std;
    int plu(int a,int b){
        if(b==0){
            return a;
        }
        else{
            int xor;
            int car;
            xor=a^b;
            car=(a&b)<<1;
            return plu(xor,car);
        }
    }
    int main(){
    	//freopen(".in","r",stdin);
        //freopen(".out","w",stdout);
        long long A;
        long long B;
        cin>>A;
        cin>>B;
        cout<<pul(A,B);
        //fclose(stdin);
        //fclose(stdout);
        return 0;
    }
    

模拟:

代码有些长,看官耐着性子看完吧QwQ

首先说下大致思路:就是通过数组记录数字每一位,再通过分类讨论实现人工加法力。

先放代码吧

Code

#include <bits/stdc++.h>
using namespace std;
long long n;
long long m;
long long ans;
inline long long read(){
	long long s=0;
	long long w=1;
	char ch=getchar();
	while(ch<'0'||ch>'9'){
		if(ch=='-'){
			w=-1;
		}
		ch=getchar();
	}
	while(ch>='0'&&ch<='9'){
		s=(s<<1)+(s<<3)+(ch^48);
		ch=getchar();
	}
	return s*w;
}
inline void write(long long s){
	if(s<0){
		putchar('-');
		s=~s+1;
	}
	if(s>9){
		write(s/10);
	}
	putchar(s%10+'0');
	return ;
}
inline long long Cdm(long long a,long long b){
	long long sa[10001];
	long long sb[10001];
	long long sj[10001];
	long long sum=0;
	long long Wei=1;
	memset(sa,0,sizeof(sa));
	memset(sb,0,sizeof(sb));
	memset(sj,0,sizeof(sj));
	long long s;
	long long w;
	s=a;
	w=b;
	long long len=0;
	long long ten=0;
	bool Jin_Wei_Yes=false;
	while(s){
		sa[++len]=s%10;
		s/=10;
	}
	while(w){
		sb[++ten]=w%10;
		w/=10;
	}
	long long Chang=max(len,ten);
	for(long long i=1;i<=Chang;i++){
		sj[i]+=sa[i]+sb[i];
		if(sj[i]>=10){
			sj[i+1]+=1;
			sj[i]%=10;
		}
		if(i==Chang){
			if(sj[i+1]!=0){
				Jin_Wei_Yes=true;
				break;
			}
		}
	}
	if(Jin_Wei_Yes==true){
		Chang++;
	}
	else;
	for(long long i=1;i<=Chang;i++){
		sum+=sj[i]*Wei;
		Wei*=10;
	}
	return -sum;
}
inline long long Usd(long long a,long long b){
	long long sa[10001];
	long long sb[10001];
	long long sj[10001];
	long long sum=0;
	long long Wei=1;
	memset(sa,0,sizeof(sa));
	memset(sb,0,sizeof(sb));
	memset(sj,0,sizeof(sj));
	long long s;
	long long w;
	bool Fu=false;
	s=a;
	w=b;
	long long len=0;
	long long ten=0;
	bool Jin_Wei_Yes=false;
	while(s){
		sa[++len]=s%10;
		s/=10;
	}
	while(w){
		sb[++ten]=w%10;
		w/=10;
	}
	long long Chang=max(len,ten);
	for(long long i=1;i<=Chang;i++){
		sj[i]+=sa[i]-sb[i];
		if(sj[i]<0){
			if(i!=Chang){
				sj[i+1]-=1;
				sj[i]+=10;
			}
			else{
				break;
			}
		}
		if(i==Chang){
			if(sj[i+1]!=0){
				Jin_Wei_Yes=true;
				break;
			}
		}
	}
	if(Jin_Wei_Yes==true){
		Chang++;
		Fu=true;
	}
	else;
	if(Fu==true){
		sj[Chang]=~sj[Chang]+1;
		for(long long i=1;i<=Chang;i++){
			sum+=sj[i]*Wei;
			Wei*=10;
		}
		return -sum;
	}
	else{
		for(long long i=1;i<=Chang;i++){
			sum+=sj[i]*Wei;
			Wei*=10;
		}
		return sum;
	}
}
inline long long Opk(long long a,long long b){
	long long sa[10001];
	long long sb[10001];
	long long sj[10001];
	long long sum=0;
	long long Wei=1;
	memset(sa,0,sizeof(sa));
	memset(sb,0,sizeof(sb));
	memset(sj,0,sizeof(sj));
	long long s;
	long long w;
	s=a;
	w=b;
	long long len=0;
	long long ten=0;
	bool Jin_Wei_Yes=false;
	while(s){
		sa[++len]=s%10;
		s/=10;
	}
	while(w){
		sb[++ten]=w%10;
		w/=10;
	}
	long long Chang=max(len,ten);
	for(long long i=1;i<=Chang;i++){
		sj[i]+=sa[i]+sb[i];
		if(sj[i]>=10){
			sj[i+1]+=1;
			sj[i]%=10;
		}
		if(i==Chang){
			if(sj[i+1]!=0){
				Jin_Wei_Yes=true;
				break;
			}
		}
	}
	if(Jin_Wei_Yes==true){
		Chang++;
	}
	else;
	for(long long i=1;i<=Chang;i++){
		sum+=sj[i]*Wei;
		Wei*=10;
	}
	return sum;
}
inline long long Adm(long long a,long long b){
	bool _a=false;
	bool _b=false;
	if(a<0){
		_a=true;
	}
	if(b<0){
		_b=true;
	}
	if(_a==true&&_b==true){
		return Cdm(-a,-b);
	}
	else{
		if(_a==true&&_b==false){
			return Usd(b,-a);
		}
		else{
			if(_a==false&&_b==true){
				return Usd(a,-b);
			}
			else{
				if(_a==false&&_b==false){
					return Opk(a,b);
				}
			}
		}
	}
}
int main(){
	//freopen(".in","r",stdin);
	//freopen(".out","w",stdout);
	n=read();
	m=read();
	ans=Adm(n,m);
	write(ans);
	//fclose(stdin);
	//fclose(stdout);
	return 0;
}

再来说说实现吧:

  1. Adm函数:

    就是加法函数呢,现在来解释下里面执行语句的意思吧:

    _a_b都是用来记录两个加数是否为负数,所以就定义成bool类型啦。

    那么我们可以发现有下面几种情况:

    • ab皆为正:

      这时候就是普通的加法啦,没啥好说的直接略过吧QAQ

    • a为正,b为负:

      这时候就要转换一下啦(因为我的加法模拟函数没写负数情况呢QwQ

      a + b = a - ( - b ) ;

      因为b是负数,那- b就肯定是正数啦!现在再写一个正数减法函数就好了捏。

    • a为负,b为正:

      仍然很简单捏,来转换一下柿子就好啦:

      a + b = b + a = b - ( - a ) ;

      因为a是负数,那- a肯定是正数啦,又是一个正数减法,太简单啦!

    • ab皆为负:

      其实这种情况最简单啦,再来转换下吧:

      a + b = - ( ( - a ) + ( - b ) );

      因为ab都是负数,那- a- b就都是正数啦!直接当做正数加法做在输出结果相反数就好啦!

  2. 既然分类讨论了,那我们不妨看看加法模拟函数怎么写吧:

    Code

    inline long long Opk(long long a,long long b){
    	long long sa[10001];
    	long long sb[10001];
    	long long sj[10001];
    	long long sum=0;
    	long long Wei=1;
    	memset(sa,0,sizeof(sa));
    	memset(sb,0,sizeof(sb));
    	memset(sj,0,sizeof(sj));
    	long long s;
    	long long w;
    	s=a;
    	w=b;
    	long long len=0;
    	long long ten=0;
    	bool Jin_Wei_Yes=false;
    	while(s){
    		sa[++len]=s%10;
    		s/=10;
    	}
    	while(w){
    		sb[++ten]=w%10;
    		w/=10;
    	}
    	long long Chang=max(len,ten);
    	for(long long i=1;i<=Chang;i++){
    		sj[i]+=sa[i]+sb[i];
    		if(sj[i]>=10){
    			sj[i+1]+=1;
    			sj[i]%=10;
    		}
    		if(i==Chang){
    			if(sj[i+1]!=0){
    				Jin_Wei_Yes=true;
    				break;
    			}
    		}
    	}
    	if(Jin_Wei_Yes==true){
    		Chang++;
    	}
    	else;
    	for(long long i=1;i<=Chang;i++){
    		sum+=sj[i]*Wei;
    		Wei*=10;
    	}
    	return sum;
    }
    

    看完代码,来具体看一下这是怎样实现的。

    • 首先,让我们看一下变量和数组:

      • sa[]sb[]:记录加数的每一位;
      • sj[]:记录结果的每一位;
      • Fu:记录结果是否为负;
      • Chang:记录最大长度;
      • Jin_Wei_Yes:记录最后是否进位;
    • 然后,来看下实现方式:

      先把两个加数的每一位记录下来:

      while(s){
      	sa[++len]=s%10;
      	s/=10;
      }
      while(w){
      	sb[++ten]=w%10;
      	w/=10;
      }
      

      接着,记录最大长度,从最后一位开始运算,方便进位:

      long long Chang=max(len,ten);
      for(long long i=1;i<=Chang;i++){
      	sj[i]+=sa[i]+sb[i];
      	if(sj[i]>=10){
      		sj[i+1]+=1;
      		sj[i]%=10;
      	}
      	if(i==Chang){
      		if(sj[i+1]!=0){
      			Jin_Wei_Yes=true;
      			break;
      		}
      	}
      }
      

      重点看下进位与否,进位后最大长度要自加:

      if(Jin_Wei_Yes==true){
      	Chang++;
      }
      else;
      

​ 最后把每一位加上再输出就好啦!

​ 其他函数都是一样的,这里就不多说了。

快读优化:

其实没啥,这里就讲下位运算版快读和快写吧:

快读:

  • 代码:
inline int read(){
	int s=0;
	int w=1;
	char ch;
	ch=getchar();
	while(ch<'0'||ch>'9'){
		if(ch=='-'){
			w=-1;
		}
		ch=getchar();
	}
	while(ch>='0'&&ch<='9'){
		s=(s<<1)+(s<<3)+(ch^48);
		ch=getchar();
	}
	return s*w;
}
  • 思想:

s 作为基数即最后返回的数w 用来记录是否为负数ch 记录数字每一位,用 getchar 输入。与普通版不同的是这句话:

  1. 原版:
    s=s*10+ch-'0';
  1. 位运算版:
    s=(s<<1)+(s<<3)+(ch^48);

众所周知,位运算要比加减乘除要快得多,由原版到位运算的运算过程大致如下:

(s<<1)+(s<<3)=s*2+s*2*2*2
			 =2*s*(1+2*2)
			 =2*5*s
			 =s*10

快写:

  • 代码:
inline void write(int s){
    int len=0;
    char ch[20];
    if(s<0){
        putchar((1<<5)+(1<<3)+(1<<2)+1);
        s=~s+1;
    }
    do{
        ch[len++]=s%10+(1<<4)+(1<<5);
        s/=10;
    }while(s>0);
    for(int i=len-1;i>=0;i--){
        putchar(ch[i]);
    }
    return ;
}
  • 思想:

与普通版唯一不同的是:

  1. 实现方式:
  • 原版:递归

  • 位运算版:循环

  1. 判断负数:
  • 原版:直接输出符号

  • 位运算版:输出符号的ASCLL

  1. 记录:
  • 原版:无需记录,直接输出

  • 位运算版:用字符数组记录数字每一位的ASCLL码,输出时强转 int

讲的也不少了,就写到这力。

posted @ 2023-03-17 17:00  Hzzxx  阅读(26)  评论(0编辑  收藏  举报