AcWing基础算法(二)
高精度整数加法
题目
给定两个正整数(不含前导 0),计算它们的和。
输入格式
共两行,每行包含一个整数。
输出格式
共一行,包含所求的和。
数据范围
1≤整数长度≤1000001≤整数长度≤100000
输入样例:
12
23
输出样例:
35
思路
既然题目给定的数字很长int存不了的话,那我们就改用数组存储。因为我们要用数组存储,所以我们没办法直接相加(要不然也不会是算法),那我们可以模拟我们人类的计算过程让计算机进行计算。
实现
既然是数组存储数据我们就要想清楚到底是正着存还是倒着存,如果模拟我们人类运算的话那肯定是先做个位再做十位,那我们就要倒着存了
string a,b;
vector<int> A,B;//容器,c++所特有的,你可以把它类比与数组
cin >>a>>b;//输入数据
for(int i=a.size()-1;i>=0;i--)A.push_back(a[i]-'0');//获取字符串长度,然后倒序存入容器
for(int i=b.size()-1;i>=0;i--)B.push_back(b[i]-'0');
模拟人类加法
既然已经倒序存入数组,那么我们就开始模拟我们自己做加法的情节
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);//C为我们返回的结果,类型也是容器
//保留进位数也就是十位数
t/=10;
}
if(t)C.push_back(t);//0为false,其它为true,也就是将进位数加上去
return C;
最终代码
#include<iostream>
#include<vector>
using namespace std;
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(t);
return C;
}
int main(){
string a,b;
vector<int> A,B;
cin>>a>>b;
for(int i=a.size()-1;i>=0;i--)A.push_back(a[i]-'0');
for(int i=b.size()-1;i>=0;i--)B.push_back(b[i]-'0');
vector<int> C=add(A,B);
for(int i=C.size()-1;i>=0;i--)printf("%d",C[i]);
return 0;
}
高精度整数减法
题目
给定两个正整数(不含前导 0),计算它们的差,计算结果可能为负数。
输入格式
共两行,每行包含一个整数。
输出格式
共一行,包含所求的差。
数据范围
1≤整数长度≤1051≤整数长度≤105
输入样例:
32
11
输出样例:
21
思路
和加法一样倒序将数据存入数组中,然后模拟人类做减法
实现
先判断输入的A,B两个数据谁大,如果A大就用A-B,如果B大就用-(A-B)
bool flag(vector<int> &A,vector<int> &B){
//判断两个长度是否一样
if(A.size()!=B.size())return A.size()>B.size();
//从大到小判断A,B哪个大
else for(int i=A.size()-1;i>=0;i--){
if(A[i]!=B[i])return A[i]>=B[i];
}
//一样大的话返回
return true;
}
模拟人类相减
因为会存在当前位数不够减借位的情况,所以定义一个整数t表示是否借位
int t=0;
for(int i=0;i<A.size();i++){
t=A[i]-t;//表示当前位数减去上一期前一位借的数,也就是当前为数实际剩余多少
if(i<B.size())t=t-B[i];
C.push_back((t+10)%10);//如果t小于0的话则加上10返回的是借位完成运算的数,如果t大于0则加上10在模10余下的还是原来相减的值不变
if(t>=0)t=0;
else t=1;
}
//最后不要忘记将前面没有用的0删掉c.back()返回当前最后一位数,C。pop_back()表示删除当前数
while(C.size()>1&&C.back()==0)C.pop_back();
return C;
最终代码
#include<iostream>
#include<vector>
using namespace std;
bool cmp(vector<int> &A,vector<int> &B){
if(A.size()!=B.size())return A.size()>B.size();
else for(int i=A.size()-1;i>=0;i--){
if(A[i]!=B[i])return A[i]>=B[i];
}
return true;
}
vector<int> sub(vector<int> &A,vector<int> &B){
vector<int> C;
int t=0;
for(int i=0;i<A.size();i++){
t=A[i]-t;
if(i<B.size())t-=B[i];
C.push_back((t+10)%10);
if(t>=0)t=0;
else t=1;
}
while(C.size()>1&&C.back()==0)C.pop_back();
return C;
}
int main(){
string a,b;
vector<int> A,B;
cin>>a>>b;
for(int i=a.size()-1;i>=0;i--)A.push_back(a[i]-'0');
for(int i=b.size()-1;i>=0;i--)B.push_back(b[i]-'0');
vector<int> C;
if(cmp(A,B)){
C=sub(A,B);
for(int i=C.size()-1;i>=0;i--)printf("%d",C[i]);
}
else{
C=sub(B,A);
printf("-");
for(int i=C.size()-1;i>=0;i--)printf("%d",C[i]);
}
return 0;
}
高精度乘法
题目
给定两个非负整数(不含前导 0) A 和 B,请你计算 A×B 的值。
输入格式
共两行,第一行包含整数 A,第二行包含整数 B。
输出格式
共一行,包含 A×B 的值。
数据范围
1≤A的长度≤1000001≤A的长度≤100000,
0≤B≤100000≤B≤10000
输入样例:
2
3
输出样例:
6
思路
和加法一样先将数据倒序存入数组,然后用当前位数乘以较小的那个数再做模得到余数,然后将剩余的进位在下次乘法的时候使用。
int t=0;
for(int i=0;i<A.size()||t;i++){//t不等于0但是i超过A的大小的时候说明此时只有进位,依然要进行运算
if(i<A.size())t+=A[i]*b;
C.push_back(t%10);
t/10=;
}
最终代码
#include<iostream>
#include<vector>
using namespace std;
vector<int> mul(vector<int> &A,int b){
vector<int> C;
int t=0;
for(int i=0;i<A.size()||t;i++){
if(i<A.size())t+=A[i]*b;
C.push_back(t%10);
t/=10;
}
while(C.size()>1&&C.back()==0)C.pop_back();
return C;
}
int main(){
string a;
int b;
vector<int> A,C;
cin>>a>>b;
for(int i=a.size()-1;i>=0;i--)A.push_back(a[i]-'0');
C=mul(A,b);
for(int i=C.size()-1;i>=0;i--)printf("%d",C[i]);
return 0;
}
高精度除法
题目
给定两个非负整数(不含前导 00) A,BA,B,请你计算 A/BA/B 的商和余数。
输入格式
共两行,第一行包含整数 A,第二行包含整数 B。
输出格式
共两行,第一行输出所求的商,第二行输出所求余数。
数据范围
1≤A的长度≤1000001≤A的长度≤100000,
1≤B≤100001≤B≤10000,
B 一定不为 0
输入样例:
7
2
输出样例:
3
1
思路
模拟我们做除法是情形,我们是从打到小进行运算的,和前面三个不一样,但是在做题的时候一般是这四个运算混合在一起,所以我们还是将数据倒着存入数组。
vector<int> C;
int r=0;
for(int i=A.size()-1;i>=0;i--){
r=r*10+A[i];
C.push_back(r/b);
r%=b;
}
reverse(C.begin(),C.end());//将结果调换
//消除0
while(C.size()>1&&C.back()==0)C.pop_back();
最终代码
#include<iostream>
#include<algorithm>//使用reverse的头文件
#include<vector>
using namespace std;
vector<int> div(vector<int> &A,int b,int &r){
vector<int> C;
r=0;
for(int i=A.size()-1;i>=0;i--){
r=r*10+A[i];
C.push_back(r/b);
r%=b;
}
reverse(C.begin(),C.end());
while(C.size()>1&&C.back()==0)C.pop_back();
return C;
}
int main(){
string a;
int b,r;
cin>>a>>b;
vector<int> A,C;
for(int i=a.size()-1;i>=0;i--)A.push_back(a[i]-'0');
C=div(A,b,r);
for(int i=C.size()-1;i>=0;i--)printf("%d",C[i]);
cout<<endl<<r;
return 0;
}
前缀和
题目
输入一个长度为 n 的整数序列。
接下来再输入 m 个询问,每个询问输入一对 l,r。
对于每个询问,输出原序列中从第 l 个数到第 r 个数的和。
输入格式
第一行包含两个整数 n 和 m。
第二行包含 n 个整数,表示整数数列。
接下来 mm 行,每行包含两个整数 l 和 r,表示一个询问的区间范围。
输出格式
共 mm 行,每行输出一个询问的结果。
数据范围
1≤l≤r≤n1≤l≤r≤n,
1≤n,m≤1000001≤n,m≤100000,
−1000≤数列中元素的值≤1000−1000≤数列中元素的值≤1000
输入样例:
5 3
2 1 3 6 4
1 2
1 3
2 4
输出样例:
3
6
10
思路
想必大家高中也肯定学过数列,前缀和就是前n项之和,使用for就可以解决。但要注意,我们通常会将S0定义为0,真正的数据从S1才开始放入,这样的好处就是在做r-m时,我们所要进行的是Sr-Sm-1,如果m=1的时候直接将S0带入就可以了,不需要我们再额外处理。
#include<iostream>
using namespace std;
const int N=100010;
int a[N],b[N];
int main(){
int m,n;
scanf("%d%d",&n,&m);
b[0]=0;
for(int i=1;i<=n;i++){
scanf("%d",&a[i]);
b[i]=b[i-1]+a[i];
}
while(m--){
int l,r;
scanf("%d%d",&l,&r);
printf("%d",b[r]-b[l-1]);
}
return 0;
}
子矩阵的和
题目
输入一个 nn 行 mm 列的整数矩阵,再输入 qq 个询问,每个询问包含四个整数 x1,y1,x2,y2x1,y1,x2,y2,表示一个子矩阵的左上角坐标和右下角坐标。
对于每个询问输出子矩阵中所有数的和。
输入格式
第一行包含三个整数 n,m,q。
接下来 n 行,每行包含 m 个整数,表示整数矩阵。
接下来 q 行,每行包含四个整数 x1,y1,x2,y2,表示一组询问。
输出格式
共 q 行,每行输出一个询问的结果。
数据范围
1≤n,m≤10001≤n,m≤1000,
1≤q≤2000001≤q≤200000,
1≤x1≤x2≤n1≤x1≤x2≤n,
1≤y1≤y2≤m1≤y1≤y2≤m,
−1000≤矩阵内元素的值≤1000
输入样例:
3 4 3
1 7 2 4
3 6 2 8
2 1 2 3
1 1 2 2
2 1 3 4
1 3 3 4
输出样例:
17
27
21
思路
与前缀和类似,如果求A1B1与A2B2组成的矩形,只需要用整个大的红颜色矩形减去灰色小矩形,再减去绿色小矩形,再将绿色与灰色相交的一小块相加就可以了。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-bXl6wkrq-1643126558942)(C:\Users\cxy\AppData\Roaming\Typora\typora-user-images\image-20220125202640633.png)]
实现
#include<iostream>
using namespace std;
const int N=1010;
int s[N][N],a[N][N];
int main(){
int n,m,q;
scanf("%d%d%d",&n,&m,&q);
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
scanf("%d",&a[i][j]);
}
}
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
s[i][j]+=s[i-1][j]+s[i][j-1]-s[i-1][j-1]+a[i][j];//前置和
}
}
while(q--){
int x1,y1,x2,y2;
scanf("%d%d%d%d",&x1,&y1,&x2,&y2);
printf("%d\n",s[x2][y2]-s[x1-1][y2]-s[x2][y1-1]+s[x1-1][y1-1]);//求结果
}
return 0;
}
差分
题目
输入一个长度为 n 的整数序列。
接下来输入 m 个操作,每个操作包含三个整数 l,r,c,表示将序列中 [l,r] 之间的每个数加上 c。
请你输出进行完所有操作后的序列。
输入格式
第一行包含两个整数 n 和 m。
第二行包含 n 个整数,表示整数序列。
接下来 mm 行,每行包含三个整数 l,r,c表示一个操作。
输出格式
共一行,包含 n 个整数,表示最终序列。
数据范围
1≤n,m≤1000001≤n,m≤100000,
1≤l≤r≤n1≤l≤r≤n,
−1000≤c≤1000−1000≤c≤1000,
−1000≤整数序列中元素的值≤1000
输入样例:
6 3
1 2 2 1 2 1
1 3 1
3 5 1
1 6 1
输出样例:
3 4 5 3 4 2
思路
用一个数组表示数据数据的每项的和,也就是a5=b1+b2+b3+b4+b5,当让对l,r区间内的数进行加法运算时,只需要在bl处加上该值,在r+1处减去该值,就可以使l,r区间内的数进行加法运算。
#include<iostream>
using namespace std;
const int N=100010;
int q[N],b[N];
int insert(int l,int r,int c){
b[l]+=c;
b[r+1]-=c;
}
int main(){
int n,m;
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)scanf("%d",&q[i]);
for(int i=1;i<=n;i++)insert(i,i,q[i]);//使b数组变成q数组元素的n项和
while(m--){
int l,r,c;
scanf("%d%d%d",&l,&r,&c);
insert(l,r,c);
}
for(int i=1;i<=n;i++)b[i]+=b[i-1];
for(int i=1;i<=n;i++)printf("%d ",b[i]);
}
差分矩阵
题目
输入一个 n 行 m 列的整数矩阵,再输入 q 个操作,每个操作包含五个整数x1,y1,x2,y2,c,其中 (x1,y1) 和(x2,y2) 表示一个子矩阵的左上角坐标和右下角坐标。
每个操作都要将选中的子矩阵中的每个元素的值加上 c。
请你将进行完所有操作后的矩阵输出。
输入格式
第一行包含整数 n,m,q。
接下来 n 行,每行包含 m 个整数,表示整数矩阵。
接下来 q 行,每行包含 5 个整数 x1,y1,x2,y2,c表示一个操作。
输出格式
共 n 行,每行 m 个整数,表示所有操作进行完毕后的最终矩阵。
数据范围
1≤n,m≤1000,
1≤q≤100000,
1≤x1≤x2≤n,
1≤y1≤y2≤m,
−1000≤c≤1000,
−1000≤矩阵内元素的值≤1000
输入样例:
3 4 3
1 2 2 1
3 2 2 1
1 1 1 1
1 1 2 2 1
1 3 2 3 2
3 1 3 4 1
输出样例:
2 3 4 1
4 3 4 1
2 2 2 2
思路
与一维的差分一样,还是将b数组定义为a数组元素的前n项和。
实现
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-NrkL6rEn-1643126558943)(C:\Users\cxy\AppData\Roaming\Typora\typora-user-images\image-20220125231824255.png)]
将x1y1加上c,那么x1y1后面的所有数都加上了c,那么就需要减去绿颜色的矩形和蓝颜色的矩形,再加上蓝绿相交的矩形
#include<iostream>
using namespace std;
const int N=1010;
int s[N][N],b[N][N];
void insert(int x1,int y1,int x2,int y2,int c){
b[x1][y1]+=c;
b[x2+1][y1]-=c;
b[x1][y2+1]-=c;
b[x2+1][y2+1]+=c;
}
int main(){
int n,m,q;
scanf("%d%d%d",&n,&m,&q);
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
scanf("%d",&s[i][j]);
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
insert(i,j,i,j,s[i][j]);
while(q--){
int x1,x2,y1,y2,c;
cin >> x1 >> y1 >> x2 >> y2 >> c;
insert(x1,y1,x2,y2,c);
}
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
b[i][j]+=b[i-1][j]+b[i][j-1]-b[i-1][j-1];
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++)printf("%d ",b[i][j]);
puts("");
}
return 0;
}
td;
const int N=1010;
int s[N][N],b[N][N];
void insert(int x1,int y1,int x2,int y2,int c){
b[x1][y1]+=c;
b[x2+1][y1]-=c;
b[x1][y2+1]-=c;
b[x2+1][y2+1]+=c;
}
int main(){
int n,m,q;
scanf("%d%d%d",&n,&m,&q);
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
scanf("%d",&s[i][j]);
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
insert(i,j,i,j,s[i][j]);
while(q--){
int x1,x2,y1,y2,c;
cin >> x1 >> y1 >> x2 >> y2 >> c;
insert(x1,y1,x2,y2,c);
}
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
b[i][j]+=b[i-1][j]+b[i][j-1]-b[i-1][j-1];
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++)printf("%d ",b[i][j]);
puts("");
}
return 0;
}