AcWing基础算法(二)

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;

}
































  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值