【2023.03.17】Luogu P1001 A+B Problem 题解
P1001 A+B Problem
在此声明:本人在首A此题时,确实是一个萌新。那么,让我们以萌新的角度,看一下这道题:
首先,题目大意:
输入一个A,再输入一个B,求出A与B的和,输出。
嗯,题目非常有深意,我们不妨来一步一步做:
-
头文件:作为萌新,我们必须来认识C++的头文件,头文件定义多少直接关系到我们能够调用多少函数,我们在这里需要的函数只有主函数和
cin
以及cout
。直接定义iostream
库就可以啦!#include <iostream>
-
指明:作为萌新,我们有必要背过并熟练掌握这句语句,Ta相当于一把万能钥匙,可以在我们主函数内调用函数时使用
std
。如果没有写,那么我们在主函数里调用std
的函数时就必须在前面加上std::
唷!using namespace std;
-
主函数:终于写主函数了呢,作为萌新,我们一般会用这样的主函数:
int main(){ }
然而,主函数并不是只有这一种写法,在此再放出两种:
signed main(){ }
int main(void){ }
我们可以任意用一种呢,不过在
main
前面,是不能用long long
的哦。 -
定义:作为萌新,我们自然知道如何定义一个变量:
类型名 变量名;
不过在此建议,变量名最好有一定的含义,不然容易弄混呢。
在这里,我们注意看数据范围:
|a|,|b| ≤ 10 ^ 9
显然,
int
类型完全可以驾驭,不过保险起见,我们还是定义成long long
类型吧。long long A,B;
-
输入:作为萌新,我们深知
cin
的魅力,所以在这里我们就用cin
和cout
啦!cin>>A>>B;
-
计算 & 输出:作为萌新,我们自然知道在C++中,一部分运算符和我们日常数学上的运算符是一样的,加号就是如此。那么我们直接在输出对象运算就可以啦!
cout<<A+B;
-
结束程序:作为萌新,我们一般都具有
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:这个还是会一点的,不过下面就不想讲了呢
⑦ 字典树:蒟蒻并不会
⑧ 位运算:下面也会讲力
⑨ 模拟:这是我最喜欢的呢,下面也会讲力
⑩ 快速读入优化:其实本人并不认为这是种解法,但是这位写了,就讲下力
位运算:
先说一下为什么要用位运算:因为位运算的速度远快于普通运算,所以就用位运算力。
大体思路就是模拟十进制竖式加法,先利用异或运算算出不进位的值,再通过与运算算出进位的值。递归调用之后加起来输出就好啦!
那么接下来,我们一步步看吧!
-
模板 & 定义 & 输入:
就不多讲了呢,直接放代码吧:
#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; }
-
正题:大体思路已经讲了,那就直接放代码实现吧:
位运算加法函数:
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;
}
再来说说实现吧:
-
Adm
函数:就是加法函数呢,现在来解释下里面执行语句的意思吧:
_a
和_b
都是用来记录两个加数是否为负数,所以就定义成bool
类型啦。那么我们可以发现有下面几种情况:
-
a
和b
皆为正:这时候就是普通的加法啦,没啥好说的直接略过吧
QAQ
。 -
a
为正,b
为负:这时候就要转换一下啦(
因为我的加法模拟函数没写负数情况呢)QwQ
a + b = a - ( - b ) ;
因为
b
是负数,那- b
就肯定是正数啦!现在再写一个正数减法函数就好了捏。 -
a
为负,b
为正:仍然很简单捏,来转换一下柿子就好啦:
a + b = b + a = b - ( - a ) ;
因为
a
是负数,那- a
肯定是正数啦,又是一个正数减法,太简单啦! -
a
和b
皆为负:其实这种情况最简单啦,再来转换下吧:
a + b = - ( ( - a ) + ( - b ) );
因为
a
、b
都是负数,那- a
、- b
就都是正数啦!直接当做正数加法做在输出结果相反数就好啦!
-
-
既然分类讨论了,那我们不妨看看加法模拟函数怎么写吧:
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
输入。与普通版不同的是这句话:
- 原版:
s=s*10+ch-'0';
- 位运算版:
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 ;
}
- 思想:
与普通版唯一不同的是:
- 实现方式:
-
原版:递归
-
位运算版:循环
- 判断负数:
-
原版:直接输出符号
-
位运算版:输出符号的
ASCLL
码
- 记录:
-
原版:无需记录,直接输出
-
位运算版:用字符数组记录数字每一位的
ASCLL
码,输出时强转int
讲的也不少了,就写到这力。
本文来自博客园,作者:Hzzxx,转载请注明原文链接:https://www.cnblogs.com/xinao2186182144/p/17227413.html